• Aucun résultat trouvé

3.2 Langages Simulink et Stateflow

3.2.3 L’ordonnancement en Simulink

Nous présentons dans cette section plusieurs exemples afin d’illustrer l’ordon- nancement des blocs qui constituent les circuits Simulink. Les circuits repré- sentent des possibilités d’exécution concurrente, comme tout modèle flot de données. Cependant, Simulink a une sémantique d’exécution séquentielle ne permettant pas la concurrence.

Pour transformer un circuit en code séquentiel, il faut associer un ordre d’exécution unique pour chacun des blocs du circuit. Cet ordre dépend de la structure de ce dernier et des règles d’ordonnancement.

D’une part, l’ordonnancement des blocs Simulink consiste à respecter la causalité des données et du contrôle, c’est-à-dire suivre la structure du circuit selon les flots de données et de contrôle. Autrement dit, pour un bloc donné, les valeurs des signaux de données connectés sur ses port d’entrée doivent être calculées avant l’exécution de ce bloc, et les blocs contrôlés sont exécutés pendant l’exécution des blocs contrôleurs.

D’autre part, des contraintes additionnelles s’ajoutent pour assurer un ordre total des blocs, c’est-à-dire que chaque bloc a un ordre unique. Il s’agit ici des priorités que l’utilisateur peut associer aux blocs du circuit afin de forcer explicitement l’exécution d’une partie du circuit avant une autre. En pratique, un bloc peut posséder deux priorités : explicite qui est définie par l’utilisateur, et implicite qui est déduite de la position graphique du bloc. Le bloc le plus prioritaire selon la priorité implicite est celui se trouvant le plus en haut à gauche.

Afin d’introduire les contraintes d’ordonnancement, nous partons d’un cas simple de circuits aux flots de données sans blocs séquentiels (à mémoire), puis nous considérons les circuits aux flots de données avec blocs séquentiels (à mémoire) pour ensuite traiter le cas plus complexe contenant des flots de données avec des imbrications de flots de contrôle.

3.2.3.1 Circuits aux flots de données sans mémoire

L’ordonnancement des blocs dont les entrées ne sont reliées qu’à des signaux de données suit uniquement la propagation des données à travers le circuit. Il faut exécuter les blocs qui alimentent les entrées d’un bloc avant d’exécuter celui-ci. Un exemple de circuit à flots de données est illustré par la figure3.13. Les blocs Devide et Gain1, qui produisent les données pour les entrées du bloc Add, doivent être exécutés avant le bloc Add. Un circuit à flots de données exprime une exécution concurrente potentielle entre les blocs.

Out2 2 Out1 1 Gain2 1 Gain1 1 Gain 1 Divide Constant 1 Add In1 1

Fig. 3.13 – Circuit Simulink à flot de données sans mémoire

Considérons maintenant le circuit de la figure3.14. Les entrées du bloc Add sont connectées sur la sortie du bloc In1 et sur celle de Gain1 dont les données

sont produites dans le même cycle. Or la sortie du bloc Gain1 n’est produite que si le bloc Add est exécuté dans le même cycle. Ainsi, l’exécution du bloc Addnécessite d’exécuter le bloc In1 et le bloc Add et ceci dans le même cycle. Il y a donc un problème de causalité de données. Ce problème de causalité est appelé boucle algébrique de données. Pour couper ce cycle qui existe autour du bloc Add, il est nécessaire d’insérer un bloc séquentiel (à mémoire) dans ce cycle. La section suivante présente comment ordonnancer des circuits qui comporte des blocs séquentiels (à mémoire).

Gain1 10 Gain 0.5 Add Abs |u| In1 1

Fig. 3.14 – Circuit à boucle algébrique

3.2.3.2 Circuits aux flots de données avec mémoire

Les blocs séquentiels nécessitent un traitement particulier pour l’ordonnance- ment puisqu’ils dépendent des valeurs calculées dans certains cycles antérieurs. Considérons le circuit de la figure3.15présenté précédemment. Dans un cycle n, la sortie du bloc Unit Delay reçoit la valeur de son entrée durant le cycle n−1. Donc, le bloc Add ne peut être exécuté que si la sortie du bloc Unit Delaya été produite. Mais ce dernier prend en entrée la sortie du bloc Add. Il y aurait un problème de causalité des données si Unit Delay ne consomme pas une valeur en mémoire (son entrée lue au cycle n−1). Ainsi, nous consi- dérons qu’un bloc séquentiel est constitué de deux sous-blocs : les sous-blocs de lecture noté R et d’écriture noté W.

Add cycle 1 Unit Delay z 1 Constant 1 cycle

Fig. 3.15 – Circuit Simulink à flots de données avec mémoire

Pour résumer, l’ordonnancement des blocs séquentiels est effectué en deux étapes (voir figure3.16) :

1. la lecture dans la mémoire d’une valeur calculée et mémorisée dans un cycle antérieur. Cette valeur est copiée sur les signaux de sortie du bloc séquentiel ;

2. puis l’écriture en mémoire de la nouvelle valeur qui sera exploitée dans les cycles suivants. Celle-ci est calculée à partir des entrées du bloc sé- quentiel dans ce cycle-ci.

Fig. 3.16 – Découpage du bloc séquentiel en deux sous-blocs

Pour l’exécution du bloc Add de l’exemple ci-dessus, il faut d’abord exécuter dans l’ordre le bloc Constant puis la partie lecture (R) du bloc Unit Delay. La sortie produite après exécution du bloc Add sera sauvegardée en mémoire par l’exécution de la partie écriture (W) du bloc Unit Delay.

Ainsi, dans le reste du manuscrit, nous considérons une sémantique discrète des circuits traités. Nous distinguons, deux catégories de blocs Simulink :

– les blocs combinatoires produisant leurs sorties en fonction de leurs en- trées et éventuellement des paramètres du bloc, durant le même cycle ; – les blocs séquentiels ou à mémoire dont les sorties sont des valeurs cal-

culées dans un cycle antérieur.

3.2.3.3 Circuits aux flots de contrôle

Les systèmes industriels utilisent très souvent des circuits à flots de contrôle. La difficulté dans l’ordonnancement de ce type de modèle est la présence des imbrications de flots de contrôle comme cela est illustré dans la figure3.17pré- sentée précédemment. Les imbrications de flots de contrôle imposent la prise en compte de la fermeture transitive des flots de contrôle : les entrées des blocs appelés (ou contrôlés) doivent être disponibles au début de l’exécution du pre- mier bloc appelant dans la chaîne des flots de contrôle. Notons que nous nous plaçons dans une restriction de Simulink telle que pour chaque sous-système, il y a un seul bloc ou sous-système qui puisse le contrôler.

La structure du code généré pour les sous-systèmes Function-Call est illustrée dans la figure3.17. Par exemple, pour exécuter le sous-système S3, il est nécessaire d’exécuter tous les sous-systèmes qu’il contrôle ainsi que les blocs qui calculent leurs entrées respectives. Comme les sous-systèmes contrôlés sont le plus souvent imbriqués, il faut que les circuits soient dépliés par le module de pré-traitement préprocesseur (voir l’architecture de GeneAuto illustrée dans la figure3.22). Par conséquent, pour exécuter le sous-système S3, il faut d’abord exécuter le bloc S1 qui contrôle le bloc S2, les blocs qui calculent les entrées du bloc S2 ainsi que les entrées du bloc S3. Rappelons, ici, qu’un bloc contrôlé est exécuté à l’intérieur du sous-système virtuel qui l’appelle. Donc, il n’est exécuté ni avant ni après ces blocs contrôleurs mais pendant l’exécution de ces derniers. La figure3.18 illustre la structure du code impératif généré pour le circuit de la figure3.17.

!"#$% &'()* * &'()+ * ,-)".(#)!/'00 !-1232.%45!6 7-)".(#)89 :)+ ;-.+ ,-)".(#)!/'00 !-1232.%45!* 7-)".(#)89 :)+ ;-.+ ;-.* /#)2.').* < /#)2.').+ 6 /='>.5!+ %+ ?@@ Sous‐système virtuel 

Fig. 3.17 – Circuit Simulink mixant flots de données et de contrôle

void S1(...){ … S2(...); … } void S2(...){ … S3(...); … } void S3(...){ … ... } main() { ... Constant1(...); Gain1(...); Constant2(...); S1(...); … Add(...); Scope(...); … }

Fig. 3.18 – Pseudo-code impératif généré pour l’exemple

Donc, pour commencer l’exécution du bloc S3 il faut préalablement : – avoir exécuté les entrées nécessaires pour l’exécution des contrôleurs suc-

– avoir exécuté In2, l’entrée de S3, qui doit être disponible lors de l’appel de S3 ;

– que les contrôleurs successifs de S3 (dans l’ordre le bloc S1 puis S2) aient commencé leur exécution.

Dans la figure 3.17, les blocs Gain1 et Gain2 sont indépendants l’un de l’autre. Il n’existe aucune contrainte issue des signaux permettant de les or- donnancer. Simulink a prévu ce cas en offrant aux concepteurs la possibilité d’associer aux blocs une priorité, notamment pour résoudre les dépendances cachées introduites par les effets de bords effectués par certains blocs de mé- moire. Dans le cas où les deux blocs possèdent des priorités explicites égales, ou si l’utilisateur n’a pas précisé de priorité, une relation d’ordre totale est exploitée en utilisant la priorité implicite calculée à partir de la position gra- phique du bloc dans le plan où le circuit est modélisé. Dans le cas où les blocs Gain1et Gain2 par exemple ont la même priorité explicite, le bloc Gain1 sera exécuté avant le bloc Gain2, car Gain1 se trouve au dessus de Gain2. Cette relation est le produit lexicographique total de l’ordre du haut vers le bas et de l’ordre de gauche vers la droite.

Remarque Dans le cadre du projet GeneAuto, il est primordial que l’ordon-

nancement produise un ordre total de manière déterministe. De plus, à un instant t donné, un seul bloc doit être exécuté à la fois. Cet ordre doit donc respecter les contraintes d’exécution du langage Simulink.

Notons également, qu’il est fastidieux de vérifier par des tests exhaustifs la correction de l’ordonnancement des modèles Simulink/Stateflow quand il s’agit de développer et de vérifier un outil dédié. La couverture raisonnable d’un outil d’ordonnancement par des cas de tests est difficile à obtenir. En effet, l’ordonnancement est une propriété globale qui ne se déduit en général pas de l’ordonnancement des parties du circuit. Un nombre de tests important est alors nécessaire pour obtenir la confiance requise à la qualification. De plus, un des objectifs est de réduire les coûts des tests tout en augmentant l’assurance sur l’ordonnanceur.

En ce qui concerne les diagrammes Stateflow, l’ordre d’exécution des états est tributaire des conditions qui guident les transitions. En revanche, le problème d’ordonnancement ne se pose pas dans ce cadre car la séman- tique d’exécution ne permet pas l’interprétation concurrente des diagrammes contrairement aux StateCharts de Harel. De plus, les diagrammes Stateflow sont intégrés dans un circuit Simulink comme des blocs atomiques.