PICOS18 > Internals

PICos & PICos18 : Internals

Ce qui suit à pour objectif de présenter l'architecture interne de PICOS, afin de permettre aux développeurs de mieux comprendre les interractions entre le noyau et les applications, pour faciler le débuggage, et enfin pour favoriser la participation au développement de nouvelles fonctionnalités de ce noyau.
En effet, ce noyau ne répond pour le moment qu'à une partie de l'ensemble des normes OSEK. Il est amené à évoluer au fil du temps, tout comme cette rubrique...


> La norme OSEK / VDX

Dans le début des années 90, des constructeurs automobiles européens cherchent à définir un noyau temps-réel pour systèmes embarqués. Des constructeurs allemands comme BMW, Bosch, Opel et Siemens créent alors la norme OSEK (Offene Systeme und deren Schnittstellen fur die Elektronik im Kraftfahrzeug). De leur côté, les sociétés françaises comme PSA et Renault développent un système similaire batisé VDX (Vehicle Distributed eXecutive).
Finalement les projets finirent par fusionner pour ne devenir qu'une seule norme : la norme OSEK / VDX.

Afin de développer le noyau PICOS18 conformément à cette norme, je me suis basé sur les 2 documents suivants :

 
La norme OSEK / VDX 2.1 révision 1
 
 
Le livre de Joseph LEMIEUX des éditions CMPbooks.
Ce livre détaille la réalisation d'un jeux de cartes sur le noyau OsekWorks de la société WindRiver.

La norme OSEK / VDX repose sur 4 sous-ensembles :

... et bientôt sur 3 sous-ensembles supplémentaires :

 

Les versions actuelles de PICos & PICos18 ne respectent que la norme OS de OSEK.
L'objectif premier est donc de répondre parfaitment à cette norme, puis d'intégrer les moyens de communications définis par COM et enfin ajouter à PICOS des services réseaux conformément à la norme NM.

 

> Performances du noyau PICos

Le noyau PICos présente les performances suivantes :

Type de noyau :
Multi-tâche préemptif
Processeurs cibles :
Familles des PIC18 - PIC24 - PIC30 et PIC33 (Microchip)
Taille du noyau (ROM) :
< 1 Ko
Taille du noyau (RAM) :
7 octets
Taille des service (ROM) :
4 Ko
Taille des services (RAM) :
121 octets
Taille de la pile hardware :
32 appels de fonctions pour toutes les tâches
Taille de la pile software :
128 octets
Temps de latence du scheduler :
24, 5 µs (Freq = 40 MHz)
Nombre de tâches :
16
Nombre d'évènements par tâche :
8
Nombre de priorités :
15
Nombre de timers logiciels :
Pas de limites
Taille (contexte+pile) d'une tâche :
entre 128 et 256 octets


Ces performances sont suffisantes pour le développements d'applications sur PIC. Le noyau, les services et la pile logicielle ne monopolisent que la première banque de RAM. Le noyau et les services prennent moins de 5 Ko de mémoire de code.

Le nombre de tâches gérables par le scheduler est de 16, ce qui n'est pas une grande limitation en soit. Créer une application avec plus de 16 tâches synchronisées et dialogant entre elles n'est pas une chose aisée ! Découper une application en 20 ou 30 tâches distinctes n'est pas forcément une bonne idée, et il vaut mieux concentrer les actions similaires au sein de même tâche (affichage sur LCD, gestion des communication, traitement du signal,..).

Enfin le temps de latence permet de belles performances pour des applications critiques. En effet le temps de latence du scheduler (temps prie par le noyau afin de déterminer la prochaine tâche active, puis de l'activer pour finalement arriver dans la tâche) est meilleure que celui de QNX sur un 386EX à 33 MHz !!

 

> La gestion des tâches

Toutes les informations concernant les tâches de l'application sont contenues sur une seule ligne de donnée en RAM. Cette zone de la RAM réservée au noyau est appelée la zone de description de tâches en RAM.

Sur la partie de droite, on retourve 8 octets (1 octet par tâche), dont 3 sont non nuls. L'application comporte donc 3 tâches. Ces tâches sont en fait classées de gauche à droite, de la plus prioritaire à la moins prioritaire. Sur 8 bits, les 4 bits de poids forts représentent l'ID de la tâche, tandis que les points faibles représentent l'état de la tâche.
Dans cet exemple issu du tutorial, nous pouvons en déduire que :

Sur la partie de gauche, on retouve encore 8 octets (1 octet par tâche), dont 3 sont non nuls. A chaque octet correspond la tâche décrite par le même octet dans la zone de droite. Sur 8 bits, les 3 bits de poids forts indiquent la priorité de la tâche, et les 5 bits restants représentent l'adresse du pointeur de pile hardware (STKPTR) de la tâche.

Au vu de l'image ci-dessus, nous pouvons dire que :

Remarque :
La zone du descripteur de tâche en RAM contient donc 2octets de description par tâche. Or ces octets sont rangés en mémoire de la tâche la plus prioritaire à la moins prioritaire. Or la norme OSEK ne précaunise pas de pouvoir manipuler la priorité des tâches en fonctionnement. Il ne devrait alors pas être nécessaire de garder en mémoire la priorité de chaque tâche ! Toutefois la norme OSEK stipule l'emploi d'un méchanisme de CEILING PROTOCOL dans le cas d'accès aux ressources (entrée en région critique), ce qui conduit à des modifications temporaire des priorités...

 

Afin de pouvoir construire la zone de description des tâches en RAM (construction effectuée par le noyau), il faut nécessairement déclarer et paramétrer les tâches de l'application dans une zone de code : c'est la zone de description des tâches en ROM (fichier taskdesc.c).

/***************************************************** 
 * ------------- TASK DESCRIPTOR  SECTION ------------ 
 ****************************************************/ 
#pragma  romdata DESC=0x50 


/***************************************************** 
 * -------------------  Led_ON task ------------------ 
 ****************************************************/  
rom_desc_tsk rom_desc_led_ON = 
{ 0x04,      /* prioinit from 0 to 7 */
  0x04,      /* stacksize  in word [32 bits] */
  0x1000,    /* adr_tsk in 16 bits */ 
  0x00,      /* state at init phase  */ 
  0x01,      /* id_tsk from 1 to 8 */ 
  0x0100     /* ctx_tsk in 16 bits */ 
};

Cet exemple est issu du tutorial. Il déclare une tâche Led_ON dont l'ID est 1. C'est la déclaration de la tâche la moins prioritaire vue précédemment.
Nous pouvons remarquer que les informations concordent avec celles de la zone de description en RAM :

En parcourant cette structure pour chaque tâche, le noyau place l'adresse du point d'entrée de la tâche directement dans la zone de la pile hardware affectée à la tâche. Pour passer du noyau à la prochaine tâche, le noyau place donc le pointeur de pile sur la valeur STKPTR mémorisée dans la zone de descrition de tâches en RAM, puis appel l'instruction RETURN.

 

> Architecture du noyau

Le noyau PICOS18 est découpé en plusieurs blocs :

Concernant le coeur du noyau :

Tout le code relatif au coeur du noyau est rassemblé dans le fichier kernel.asm.

Comme nous l'avons vu précédemment, la déclaration des tâches se fait dans une zone de description des tâches en ROM (fichier taskdesc.c). La première effectuée par le noyau lors de la phase d'INIT est donc de parser cette zone en vu d'établir la liste des tâches de l'application.

Ensuite le SCHEDULER entre en action afin de déterminer la prochaine tâche à exécuter. L'algorithme du SCHEDULER est simple : la prochaine tâche à exécuter est la tâche prête la plus prioritaire. Il n'y a aucun algorithme de vieillissement des tâches qui consisterait à prendre en compte le temps de fonctionnement des tâches dans leurs gestions.

Enfin l'action du coeur du noyau se termine par la mise en oeuvre du TASK MANAGER. Celui-ci n'a rien à voir avec le PROCESS MANAGER. Son action se borne à restituer ou sauvegarder le contexte et la zone de pile de chaque tâche. Ceci se fait au moment du basculement d'une tâche ou bien lors du saut dans une nouvelle tâche.

 

> Service PROCESS MANAGEMENT

A suivre ...

 

 

> Service EVENT MANAGEMENT

A suivre ...

 

 

> Service ALARM MANGEMENT

A suivre ...

 

 

> Service INT MANGEMENT

A suivre ...

 

 

> Service CEILING PROTOCOL

A suivre ...

 

 

> Les HOOK-ROUTINES

A suivre ...