• Aucun résultat trouvé

3.4 Caractérisation des ressources

4.2.1 Des réseaux de Petri

La programmation de l’OSoC est liée à la description des applications et des dépendances de contrôle et de données. Ainsi, le TSMU impose au reste de l’architecture son modèle de programmation. Il se trouve que l’élément principal qui le compose se programme à partir de réseaux de Petri [209]. L’architecture utilisée, qui sera présentée plus en détail par la suite, dispose d’une structure matérielle capable d’exécuter ces graphes sans transformations majeures.

Un réseau de Petri est constitué d’états et de transitions. Un état est associé à une action particulière qui peut par exemple être une tâche. Un état en cours d’exécution est identifié par un unique jeton. Une transition est une condition logique permettant le passage du jeton vers les états suivants. Par exemple, dans une divergence en ET, la validation de la transition par un évènement extérieur a pour effet de transmettre en parallèle le jeton à tous les états suivants. Les réseaux de Petri ont la particularité d’être bien adaptés à la description du contrôle et offrent la possibilité de décrire avec efficacité le parallélisme d’exécution et de contrôle. Ainsi, ces graphes décrivent explicitement toutes les dépendances de contrôle et de données qui existent entre les tâches, les processus et les applications.

L’OSoC doit conserver un modèle de programmation proche de celui initialement utilisé. Par conséquent, les dépendances et les synchronisations entre les tâches seront représentées par la suite sous forme de graphes de tâches. Ce formalisme a l’avantage de décrire avec exactitude comment les tâches interagissent entre elles et de permettre une compréhension du comportement des applications, tout en facilitant leur analyse.

Ainsi, la séparation du contrôle et du calcul permet de disposer d’un côté de graphes de dépendance et de l’autre de tâches indépendantes. Par conséquent, l’exécution d’applications sur l’OSoC nécessite une génération de code pour chacune des tâches et une transformation des réseaux de Petri en un code interprétable par l’OSoC. Le code généré dépend du type de

ressource requis pour l’exécution. Le choix de la ressource la plus disposée pour exécuter la tâche doit être fait avant la compilation.

4.2.2 ...à une modélisation haut niveau

En fait, l’utilisation de réseaux de Petri pour la programmation n’est pas habituelle pour les développeurs de logiciel. Pour cette raison, il est souhaitable de mettre en œuvre un niveau d’abstraction supplémentaire pour une description des applications de plus haut niveau.

Néanmoins, ceci nécessite une étape de partitionnement et d’extraction du parallélisme, afin d’identifier les relations de dépendances et l’ensemble des tâches indépendantes. Ceci consiste à former pour chaque application le plus grand nombre possible de tâches dans chaque processus, tout en respectant la nature des dépendances liées au grain de calcul du système. Ce découpage peut se faire suivant les dépendances entre les données, ou suivant les fonctionnalités des différentes tâches. La taille minimale des tâches peut également être soumise à des contraintes particulières nécessaires au bon fonctionnement du système. Les étapes d’extraction peuvent ainsi donner lieu à une représentation différente sans changement de l’ossature du code.

Comme le montre la figure 4.2-a, la conversion d’un langage itératif en un ensemble com- posé d’un réseau de Petri et de tâches indépendantes est relativement naturelle. Ainsi, la dépendance de données qui lie les tâches A1 et A2 est représentée sous la forme de deux cellules reliées entre elles par une transition. Ici la transition est la fin de l’exécution de A1 car la donnée A est alors disponible. Par conséquent, l’exécution de la tâche A2 consiste juste à attendre que la cellule correspondante soit activée et à affecter la tâche indépendante pré-compilée task_A2 sur une ressource de calcul.

Void process_A() { … A = task_A1(); B = task_A2(A); …} A1 A2 Process_A task_A1() task_A2() Void process_A() { … A = task_A1(); …} Void process_B() { … Sync(task_A1); B = task_B1(A); …} A1 Process_A task_A1() task_B1() Sy B1 Process_B A a) b) Void process_A() { … A = task_A1(); B = task_A2(A); …} A1 A2 Process_A task_A1() task_A2() Void process_A() { … A = task_A1(); …} Void process_B() { … Sync(task_A1); B = task_B1(A); …} A1 Process_A task_A1() task_B1() Sy B1 Process_B A a) b)

Fig. 4.2 – Modèle de programmation de l’OSoC avec des réseaux de Petri. On extrait d’un côté un réseau de Petri décrivant toutes les dépendances de contrôle et de données et de l’autre des tâches indépendantes. Les dépendances peuvent être décrites au sein d’un même processus (a) ou entre des processus indépendants (b).

Dans un réseau de Petri tous les mécanismes de contrôle peuvent être représentés. Il est possible de décrire des activations conditionnelles des tâches (if ) ou des boucles d’exécution (while ou for ) grâce à l’utilisation de divergences et de convergences en ET et en OU. Par ailleurs, des synchronisations et des dépendances sont envisageables entre des processus indé- pendants. Néanmoins, alors que les relations de contrôle et de données sont explicites et de nature séquentielle à l’intérieur des processus, des mécanismes de synchronisation particuliers sont nécessaires entre les processus. Ces dépendances doivent être représentées sous forme de sémaphores et non pas comme de simples liaisons (voir figure 4.2-b).

4.2.3 Synthèse

En fait, le modèle de programmation de l’architecture SCMP-LC peut être défini à différents niveaux d’abstraction (figure 4.3). Depuis le niveau fonctionnel qui ne décrit pas explicitement toutes les dépendances, au graphe de contrôle et de données qui représente tous les liens qui existent entre les processus, les tâches et les applications. Cependant, chaque nouveau niveau d’abstraction nécessite des outils logiciels particuliers pour arriver à une description sous forme de réseaux de Petri.

Fig. 4.3 – Les multiples niveaux de programmation de l’architecture SCMP-LC. Différents niveaux de description peuvent être utilisés : du niveau fonctionnel aux réseaux de Petri qui représentent toutes les dépendances de contrôle et de données entre les tâches. Les tâches extraites sont indépendantes et sont compilées pour seulement un type de ressource de calcul.

Le gain apporté par la mise en parallèle du calcul dépend principalement de l’étape de par- titionnement. Plus l’extraction de tâches indépendantes est importante et plus l’application peut être parallélisée et accélérée. L’exécution en parallèle de plusieurs applications indépen- dantes dans les futurs systèmes embarqués est bien adaptée à ce modèle de programmation. Néanmoins, l’étape d’extraction est limitée par la complexité de mise en œuvre des moyens nécessaires à la parallélisation des tâches.

Dans l’état actuel de ces travaux de thèse, seul le dernier niveau de représentation est directement exploitable par notre architecture. Seuls des outils de compilation des tâches indépendantes et de transformation des graphes d’application sont utilisés.