Vous êtes sur la page 1sur 18

Real time kernel for AVR

User Guide

M.Verlinden

Version 1.0
Real time kernel for the AVR

Legal agreement and warnings

This tiny AVR kernel with documentation is free of charge. Copyright (C)
2001 by Mark Verlinden. All rights reserved.

Redistribution and use in source and binary forms, with or without


modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright


notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
3. All advertising materials mentioning features or use of this
software must display the following acknowledgement:

This product includes software developed by M.Verlinden


and its contributors.

THIS SOFTWARE IS PROVIDED BY M.VERLINDEN AND CONTRIBUTORS


``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL M.VERLINDEN OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

This file is distributed in the hope that it will be useful, but


WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Version 1.0 Page 2 6/4/2002


Real time kernel for the AVR

1 Introduction.............................................................................................................4
1.1 Usage...............................................................................................................4
2 Features...................................................................................................................5
2.1 Supported ........................................................................................................5
2.2 To do’s ............................................................................................................5
2.3 Additional features...........................................................................................5
2.4 Known issues...................................................................................................5
3 Kernel overview ......................................................................................................6
4 Breaking complexity into easier understandable tasks..............................................6
5 System level definitions and declarations.................................................................7
6 Scheduling...............................................................................................................8
6.1 Re-entrant and thread safe..............................................................................10
7 Context switching..................................................................................................12
8 Memory management ............................................................................................14
9 Semaphore.............................................................................................................15
10 Queues...............................................................................................................15
11 Timers ...............................................................................................................16
12 Messages ...........................................................................................................16
13 Pipe lines...........................................................................................................16
14 List ....................................................................................................................17

Version 1.0 Page 3 6/4/2002


Real time kernel for the AVR

1 Introduction
This kernel is a powerful, C-written real-time kernel. Atak
is short for a (or another) tiny AVR kernel used in the
rest of this document. This kernel is highly portable,
where just two issues remain to be changed in order to port
this kernel to another mcu, namely: The context switching
and interrupt handling.

1.1 Usage
There is not one particular reason I made this kernel.
I made it to increase my knowledge within my line of work
as embedded software engineer. Or maybe a bit of lets see I
if can do this kind of thing, plus making a good base
system for my robotic ambitions and last but not least, I
love everything that has to do with programming, especially
embedded!

My initial target is my robot, which uses sensors and


actuators combined with neural networks and fuzzy logic.
This kernel should provide me the basics of a real time
system to process the tasks of my robot, like vision,
movement and behavior.

Every design decision is made with respect to the robot


system. However, this kernel should provide all the basic
services, but could here and there be tuned for my own
needs.

Version 1.0 Page 4 6/4/2002


Real time kernel for the AVR

2 Features

2.1 Supported
• AVR based kernel, but can be easily ported
• Pre-emptive scheduling
• Task management (Create, suspend and resume)
• Message queues
• Semaphores
• Static task block control
• Delays
• Re-entrant and thread safe kernel services
• Optimized for speed

2.2 To do’s
• Reduce RAM usage by specifying stack for each task
• Priority scheduling
• Dynamic stack/heap management
• Only supported by CodevisionAVR C Compiler 1.0.1.4b
• Tested only on 8515 in combination with the STK200
• Timers (Will be added in version 1.1)
• (Re)Scheduling from interrupts
• Port to AVR GCC
• Pipes

2.3 Additional features


• Tracer
• Uart driver
• Efficient double linked list (Optimized for size)

2.4 Known issues

Issues CAVR 1.0.1.4b AVR-GCC


malloc Not supported Supported
TYPE** Not supported ?
if(== == ==) Not supported ?
ATMEGA323 Not supported Supported
return of a Not supported in this ?
void* version
E-function Not supported in this Not supported
version. NA to kernel

Version 1.0 Page 5 6/4/2002


Real time kernel for the AVR

3 Kernel overview
Some important descriptions of kernel elements:

• Multitasking is the process of scheduling and


switching the cpu between tasks
• A task is a quasi-independent program that is
written in such a manner that it can be assumed
it is the only task executed by the cpu
• The kernel is the part of a multitasking system
that is responsible for the management of tasks
and communication between tasks
• The scheduler is the part that is responsible for
determining which task should run next. There are
lots of different policies of how to schedule.
Priority based, time sliced (Round robin),
shortest time completion, run until done. In
general we can speak of two types: non pre-
emptive and pre-emptive
• Context switching is simply the saving of the
current running task’s context on its stack and
restore the registers of the next runable task

4 Breaking complexity into easier understandable tasks


All the tasks used in atak have the following structure
defined in “kernel.h”.

typedef struct TCB_TYPE{


unsigned int stackPointer;
unsigned int dataStackPointer;
LIST_ITEM list;
unsigned char status;
unsigned char datastack[DATA_STACK_SIZE];
unsigned char stack[STACK_SIZE];
CONTEXT context;
TASK_ENTRY_POINT entryPoint;
struct TCB_TYPE* nextTaskSemaphore;
unsigned int delay;

}TCB;

• StackPointer, this is a pointer to the stack area in


SRAM
• DatastackPointer, this is a pointer to the datastack
area in SRAM
• List, this is a structure that is used to tie tcb’s
together in a list
• status, this is the status of the task. Sleeping,
running, delayed and destroyed

Version 1.0 Page 6 6/4/2002


Real time kernel for the AVR

• Datastack, the actual datastack area


• Stack, the actual stack area
• Context, the area where context related information is
stored like function entries, arguments etc.
• EntryPoint, the actual task entry point
• NextTaskSemaphore, pointer to the next task semaphore
• Delay is used for delaying a task for n system ticks

5 System level definitions and declarations

typedef struct SYSTEM_VAR{


unsigned int task_counter;
unsigned int system_tick;
unsigned int delay_tick;
unsigned int operationTime;
} system_var;

• task_counter is a counter that keeps track of the


total running task(s).
• system_tick is the heartbeat of the kernel used for
timeslicing and is only changed in interrupt handler.
• delay_tick is a counter used for keeping track of the
delays of task. Can be changed in resolution by the
define DELAY_RESOLUTION in the config.h file.
• OperationTime is a counter used to keep track of the
time that the unit is running.
#define atak_TASK(task, arg) void task(void *arg)

this is the definition for coding a task like a function.


The prototype definition would be:
atak_TASK(Task1, arg);

which is actually the prototype entry point for the task.


In coding,
atak_TASK(Task1,arg)
{
unsigned int x = 0;
x = arg;

while(1)
{
// do something
}
}

Version 1.0 Page 7 6/4/2002


Real time kernel for the AVR

6 Scheduling

Figure 1 The scheduler executes one task at the time provided by a


queue.

What kind of scheduling should be used? Well to decide on


this matter I looked at two general types:

Pre-emptive vs. non-pre-emptive

Non pre-emptive, also called cooperative scheduling


• simple and efficient implementation
• creates timing problems when tasks run until they are
finished
• latency is the time of the longest task

Version 1.0 Page 8 6/4/2002


Real time kernel for the AVR

Pre-emptive
• tasks are scheduled and the scheduler decides who runs
• less efficient in coding size, but more powerful
• more responsive to fast execution
• latency is much lower
• kernel functions should be re-entrant

Usually a scheduler can be made non pre-emptive with not


too much effort. Just change the time slice to something
very LONG. There are different ways to make a scheduler
pre-emptive. You could use time slicing (also called Round
Robin), priority driven or some other kind of pre-emptive
algorithm.
The initial design of my kernel will provide a round robin
scheduler. In the future I will add priorities or other
interesting scheduling policies, but for now I don’t need
much more. My system has three major tasks and maybe some
small ones. These three tasks defined as movement, behavior
and vision probably all have the same priority.

Version 1.0 Page 9 6/4/2002


Real time kernel for the AVR

6.1 Re-entrant and thread safe


As said before pre-emptive scheduling requires the kernel
to have re-entrant and thread safe functions. Re-entrance
and thread safe are both related to the way functions
handle resources. Re-entrance and thread safe are separate
concepts: a function can be either re-entrant, thread-safe,
both, or neither.
A re-entrant function does not hold static data over
successive calls, nor does it return a pointer to static
data. The caller of the function provides all data. A re-
entrant function must not call non-reentrant functions.
A thread safe function protects shared resources from
concurrent access by locks. In C, local variables are
dynamically allocated on the stack. Therefore, any function
that does not use static data or other shared resources is
trivially thread-safe.

Lets say a certain task for example creates another task


with the kernel service “CreateTask()”. Suddenly a context
switch occurs before the task is being created and another
task does the same thing. It also calls “CreateTask”. Then
two tasks are using the same code and (global) data, which
will give unpredictable errors (that is if it where not re-
entrant). A thread safe function should therefore protect
its global data or use local variables (CPU registers or
variables on the stack, which are saved during a context
switch).

Figure 2 Example,
Task A wants to change a, but Task B will do this first after sudden rescheduling.

Version 1.0 Page 10 6/4/2002


Real time kernel for the AVR

The kernel switches to another task when:


• the time slice is passed
• a semaphore suspends a task
• a task delays itself with a kernel service
• a task deletes or suspends itself

The last three items force an internal reschedule. I left


out the possibility of a forced reschedule at user level,
because the kernel should only use the forced status. If
there is absolute no other possibility, then the forced
rescheduling may be used.

Some design related issues:


• Practical Round robin time slice = 10 –100 ms time
context switch should be 0.1 – 1 ms so 1 % of time
slice
• There should be 50us between disabling and enabling
interrupts.

Version 1.0 Page 11 6/4/2002


Real time kernel for the AVR

7 Context switching

Figure 3 Task control block formatted in SRAM.

Each task has a Task Control Block assigned and holds all
information concerning task switching. The entry point is
used to pin point the task (by saving the program counter)
and its argument. The context holds the current program
counter in that context and the registers, which are saved
during a context switch. The data stack is used to
dynamically store local variables, passing functions
parameters and saving registers and SREG during interrupt
service routine. The stack is used for storing the
functions return address. The delay is used for setting a
delay time on a task.

Version 1.0 Page 12 6/4/2002


Real time kernel for the AVR

Figure 4 Interruption by context switches and interrupts.

A task can be interrupted by a context switch (A) or by a


hardware interrupt routine (B). Interrupts can also call
other code in the kernel or functional code.

Priority Interrupt defintion


1 RESET
2 EXTERNAL INT 0
3 EXTERNAL INT 1
4 TIMER 1 CAPTURE
5 TIMER 1 MATCH A
6 TIMER 1 MATCH B
7 TIMER 1 OVERFLOW
8 TIMER 0 OVERFLOW
9 SERIAL TRANSFER COMPLETE
10 UART RX COMPLETE
11 UART DATA REG. EMPTY
12 UART TX COMPLETE
13 ANALOG COMPARATOR
Figure 5 Interrupt vectors and their priorities.

Version 1.0 Page 13 6/4/2002


Real time kernel for the AVR

It could also be possible that during an interrupt B (or


another kind of delay) other interrupts occur. These are
processed in priority order when the interrupt B finishes
or could be nested interrupts when the user software
enables interrupts during the processing of interrupts. I
choose to let the interrupt do its thing and when other
interrupts occur, execute them after the first interrupt.
This is done keeping the global interrupt disabled until
return from interrupt execution. Then the interrupts are
processed in priority order like table above. The AVR MCU
uses flags for interrupt event detection and masks for
enabling specific interrupts in combination with the global
interrupt enabler.

Note: Interrupts that interrupt each other is NOT supported

8 Memory management

When creating and deleting tasks, the allocated areas in


SRAM (where the TCB are stored) should be cleared and set
free to be reused in the future. At first this sounds easy,
but to efficiently coding this you inevitable ask for some
kind of memory manager. Where should the kernel store a
task block? Especially when different sizes of workspaces
are used, you would require dynamic memory management.
You’ll need some kind of algorithm to do this for you.
There are two ways in my opinion, the simple and the hard
way!

This is my solution:

There is a list of free TCB’s, allocated for you at


startup. Here you could make some difference in workspace
size, but not too much though. You could choose for a few
large workspaces of 128 bytes and a few of 64 bytes. When
adding tasks you could find the appropriate TCB for your
task. The kernel does this for you. When no space is found
the task is not created. When deleting a task, the TCB
allocation is set in the free TCB list. A the creation of a
task, it’s TCB is deleted from the free TCB list. This is a
rather easy solution for the memory management. You could
also implement a dynamic algorithm, but that will consume
far more code and complexity. Therefore I choose the easy
implementation.

Version 1.0 Page 14 6/4/2002


Real time kernel for the AVR

TCB* current = front;

BEGIN_CRITICAL_SECTION();
for(int counter=0;x < system_counter; x++){
back = current;
currentTask = current->nextTask;
if(current->task_Status == SLEEPING &&
current->nextTask == back){
current->nextTask = back->nextTask;
free deleted task workspace!
}
}
END_CRITICAL_SECTION();
Note: this will be added in version 1.1
9 Semaphore
The structure of a semaphore definition is:

typedef struct _SEMAPHORE{


struct TCB_TYPE *front;
struct TCB_TYPE *back;
int value;
} SEMAPHORE;

A semaphore is a kernel object that is used to synchronize


and change the state of tasks. A semaphore can also be used
to control and protect shared resources.

If a semaphore is already in use, the requesting task is


suspended until its current owner releases the semaphore. A
semaphore has a signal state and a waiting state. If it is
signalled the next task that was suspended by the semaphore
will be woken up. This is done by a forced reschedule. The
waiting state suspends the requesting task. If there are
more than one requesting tasks a linked list of tasks is
created.

10 Queues
The structure of a queue definition is:

typedef struct _QUEUE{


SEMAPHORE fifoFullBlock;
SEMAPHORE queueBusyBlock;
int ii,oi,size;
void* fifo[FIFO_SIZE];
}QUEUE;

With this queue pointers items can be stored and acts like
a FIFO buffer. This queue is protected by semaphores. When
a task uses a queue that is already being used it is
suspended by the semaphore. This also occurs when the queue
is full and a task wants to add an item.
Note: Should be defined globally, this way every tasks can use it.

Version 1.0 Page 15 6/4/2002


Real time kernel for the AVR

11 Timers
Timers are provided by the kernel, which holds a list of
created timers. These timers can be started and stopped or
trigger a task to wake up from the sleep status.

Note: not yet implemented

12 Messages
The structure of a message definition is:

typedef struct _MESSAGE{


int value;
unsigned char status;
unsigned char id;
}MESSAGE;

With the message tasks can sent data to each other. Usually
the message are used with a queue, making it a message
queue. A task can read from this queue and get the
appropriate messages meant for the task.

Note: Should be defined globally or local.


Not supported in v1.0

13 Pipe lines
The structure of a pipe line definition is:

typedef struct _PIPE{


SEMAPHORE fullBlock;
SEMAPHORE busyBlock;
int ii,oi,size;
void* pipe[FIFO_SIZE];
}PIPE;

A pipe line can be used to directly communicate between


tasks. This is a fast method. Much faster than the message
queue, because there is no need to identifiy the caller and
receiver. This is already done at design time, by creating
a pipeline. Just connect tasks with a pipeline and send
data. The pipelines are also protected by semaphores.

Note: Should be defined globally, this way every tasks can use it.
Not supported in v1.0

Version 1.0 Page 16 6/4/2002


Real time kernel for the AVR

14 List
typedef struct TLIST_ITEM{
struct TLIST_ITEM *next;
struct TLIST_ITEM *previous;
void* owner;
}LIST_ITEM;

When a list is empty head, prev and


next are zero. When adding an item to
an empty list, it will point to
itself (A). The head also points to
the first added item.

When there is just one item in a


list, which is being removed (A), the
head, prev and next should be set to
zero.

In case of adding an item the head


always the first added, except when
the user software updates the head
(for example in the context switch).
The added item is always positioned
at the end of the list.

Adding an item to the end of the


list.
The head pointer to prev defines the
last item.

When removing an item, it could be


the item where the head is pointing.
That situation needs an update of the
head pointer and will be the next
item in line.

The item to be removed can also be an


arbitrarily item in the list. Then
the head needs no updating.

Version 1.0 Page 17 6/4/2002


Real time kernel for the AVR

The list code itself is very efficient in usage and adds a


powerful implementation to the kernel. Almost the entire
task management is realized with this LIST type. This list
structure is not only intended for tcb’s, but can easily be
used for other goals like a timer list. It is a little OOP
thinking, but powerful indeed. Add the type to the
structure you need to make a list from and add the pointer
to the item as owner. This way when you get an item from
the list, the programmer knows the owner. And therefor it
is known how to cast the item to a usable structure. It
also doesn’t matter what kind of list you have, the list
should be type safe.

headRunningList = headRunningList->next;
ownerTCB = headRunningList->owner;
currentRunningTask = (TCB*) ownerTCB;

The kernel has several lists for the TCB’s, called the
sleeping, running, delayed and destroyed lists. Suspending
a task will remove the task in question from the running
list and then added to the sleeping list.
Resuming a task will remove the task from the sleeping list
and then added to the running list at the end. This way the
task switching always is done on the running list. There is
no need to search for tasks that could be run. Another
solution would be one list where the just the status is
checked while context switching. But this way the context
switch time could differ and we don’t want that. This is a
design consideration for speed and response time of the
kernel. The implementation though isn’t that big and also
uses link lists with pointers, which is very efficient for
speed consideration. These lists also simplify the resume
and suspend services. It is a neat solution and not
especially bigger in code than other solutions.

Usage:
Sleeping list
Running list
Destroyed list
Delayed list
Timer list

Version 1.0 Page 18 6/4/2002

Vous aimerez peut-être aussi