Chapitre 5 Génération du code Uppaal d’un ordonnanceur
5.1.1 Le langage B0 classique
Le langage B comporte un sous-ensemble appelé B0 permettant de décrire des implantations directement traduisibles en langage exécutable. B0 peut être considéré comme un langage de programmation manip- ulant des données concrètes, contrairement à B qui est un langage de spécification et de programmation.
A partir du langage B0, l’utilisateur peut générer un code C exécutable.
Modules d’implantation
Rappelons que la méthode B est une méthode de développement basée sur le mécanisme du raffinement. Ce dernier est composé de plusieurs étapes, les étapes intermédiaires sont appelées les raffinements et la dernière étape est l’implantation. Après la phase d’implantation, nous pouvons générer le code exécutable du système logiciel.
L’implantation “importe” des spécifications de machines abstraites existantes, qui sont soit implé- mentées dans le projet, soit prédéfinies et présentes dans l’environnement, comme par exemple dans les librairies de l’outil Atelier B. Cette nouvelle clause d’importation impose des restrictions d’usage sur les variables des machines importées rendant celle-ci plus forte que la clause d’inclusion, interdite dans une implantation.
Tout comme un raffinement, une implantation possède une en-tête particulière IMPLEMENTATION. Elle doit posséder une clause REFINES, ainsi qu’une relation d’implantation dans l’invariant. Cette dernière donne la correspondance entre les niveaux abstrait et concret pour les variables à implémenter en terme de variables de machines importées et de variables concrètes. Une nouvelle clause VALUES vient s’ajouter pour définir la valeur des constantes et des ensembles. La figure 5.2 présente la structure d’une implantation.
Dans la machine d’implantation, nous devons respecter certaines règles afin de se rapprocher du code exécutable:
Une implantation utilise les opérations des machines qu’elle importe, et dont elle ne peut accéder à leurs variables que pour exprimer les invariants.
Certaines restrictions sont appliquées aux substitutions apparaissant dans une implantation afin d’assurer qu’elle pourra être traduite en un programme exécutable:
– pas de choix non déterministe ou de composition parallèle,
– les conditions dans les substitutions IF THEN ELSE sont limitées à des expressions booléennes simples,
– les opérations ne s’appliquent qu’à des variables locales ou des paramètres, – les affectations mettent en jeu des expressions simples.
Une implantation peut introduire des substitutions correspondant à des boucles WHILE, mais il faudra indiquer pour chaque boucle, un invariant et un variant. Ce dernier représente une quantité qui décroît à chaque passage dans la boucle.
Au moment de l’implantation, toutes les constantes déclarées et les ensembles non énumérés doivent être instanciés.
Substitutions autorisées
Dans une implantation, on autorise certaines substitutions et on interdit d’autres. Seules les substitutions suivantes sont autorisées:
La substitution VAR IN END permettant d’introduire des variables locales. Ces variables doivent être typées dans la substitution en apparaissant dans une substitution d’initialisation (affectation ou variable résultat d’une opération).
IMPLEMENTATION nom de l’implantation REFINES
nom de la machine raffinée SEES
noms des machines vues IMPORTS
noms des machines importées PROMOTES
opérations utilisées des machines importées EXTENDS
noms des machines etendues SETS
déclaration des ensembles CONCRETE_CONSTANTS
déclaration des constantes concrètes PROPERTIES
propriétés des constantes VALUES
valuation des constantes et des ensembles CONCRETE_VARIABLES
déclaration des variables concrètes INVARIANT
propriétés invariantes des variables déclarées et relation d’implantation avec la machine à implémenter et les machines importées
ASSERTION
nouvelles assertions de l’implantation INITIALISATION
initialisation des variables de l’implantation OPERATIONS
définition des opérations de l’implantation END
Figure 5.2: Structure d’une implantation de la méthode B
La substitution BEGIN END permettant de grouper une séquence de substitutions.
La substitution IF THEN ELSE END où la condition doit correspondre à une expression booléenne calculable.
La substitution CASE qui est une forme de SELECT mais dont les prédicats comparent des ex- pressions avec des constantes.
Une boucle WHILE précisant un variant et un invariant.
L’affectation d’une expression simple à une variable (“:=”) qui peut être une variable locale ou une variable résultat d’une opération.
La substitution appel d’opération (“< ”). Les variables résultat peuvent être soit des variables locales, soit des variables résultat de l’opération dans laquelle apparaît la substitution.
La substitution identité (“skip”) qui correspond à ne rien faire.
Toutes les autres substitutions ANY, SELECT, LET, . . . ne sont pas autorisées dans les implantations.
Exemple: Génération du code B0 de la machine istacks
Reprenons la machine istacks présentée dans le chapitre précédent, et illustrée par la figure 5.3.
MACHINE istacks(ELEM) VARIABLES istack
INVARIANT istack2iseq(ELEM) /* iseq représente une séquence injective*/
INITIALISATION istack := [] OPERATIONS
push(ee) =
PRE ee2ELEM^ee62ran(istack) THEN
istack := istack ee
END ; pop =
PRE istack6=[] THEN
istack := front(istack)
END END
Figure 5.3: istacks en B
Afin d’aboutir au code B0, il faut éliminer par raffinements successifs les séquences et les opérateurs k, pour les remplacer respectivement par des tableaux et des séquencements “;”. Ces raffinements seront décrits dans les paragraphes suivants.
Machine istacks_fun Un premier raffinement illustré par la machine istacks_fun de la figure 5.4 in- troduit de nouvelles variables avec les traitements nécessaires. Il consiste donc à remplacer la séquence injective istack par une fonction totale next: ELEM -> ELEM et à définir une variable itop contenant la tête de pile. La fonction next associe à chaque processus empilé celui qui le précède dans la pile.
Espace d’état La machine istacks_fun comprend comme on l’a déjà cité les variables itop et next, en plus d’un booléen empty déterminant si la pile est vide. Elle contient également plusieurs invariants de collage avec les données de la machine istacks:
L’invariant ista k 6= [℄ ) itop= last(ista k)indique que la variableitopcorrespond à la tête de la pileista k, lorsque cette dernière est non vide.
La propriété invariante8ii:(ii22::size(ista k))next(ista k(ii))=ista k(ii 1))exprime que lorsque la pile contient plus d’un élément, la fonction nextappliquée à l’élément de rangii n’est autre que l’élément de rangii 1.
L’invariant ista k 6= [℄ ) next(first(ista k)) = first(ista k)indique que la fonction next appliquée au premier élément de la pile correspond à cet élément.
VARIABLES
INVARIANT
itop 2 ELEM
^ next 2 ELEM --> ELEM
^ empty 2 BOOL
^ (empty = bool(istack = []))
^ (istack 6= [] ) itop = last(istack))
^ (8ii.(ii 2 2..size(istack) ) next(istack(ii)) = istack(ii-1))) ^ (istack 6= [] ) next(first(istack)) = first(istack))
Opérations L’opération push permet, si la pile est vide, de chaîner le nouvel élément ee à lui- même et de donner à itop la valeur de cet élément, tout en indiquant que la pile contient actuellement des éléments. Dans le cas contraire, next va pointer sur itop et ce dernier est actualisé à ee.
push(ee) = BEGIN
IF empty = TRUE THEN
next(ee) := ee k itop := ee k empty := FALSE
ELSE
next(ee) := itop k itop := ee
END END
Dans l’opération pop, si on a plus qu’un seul élément, c’est à dire siitop6=next(itop), la variable itop prend la valeur du next. Sinon la valeur de empty devient vraie.
pop = BEGIN
IF itop 6= next(itop) THEN
itop := next(itop) ELSE
empty := TRUE END
END
Machine istacks_seq Ce raffinement présenté par la figure 5.5 consiste à éliminer les opérateurs k en les remplaçant par des ;afin d’exprimer l’enchaînement des actions de chaque opération. Ceci va nous permettre de réduire le non déterminisme des actions et il n’affecte pas la structure générale de la machine. Le non déterminisme ne peut pas être complètement éliminé ici puisqu’aucune valeur par défaut de type ELEM n’est définie pour itop.
Machine istacks_implementation L’Atelier B contient dans sa librairie des machines de base comme par exemple celles définissant les tableaux génériques. La taille de ces tableaux dépend des paramètres de la machine. Une des machines de base est BASIC_ARRAY_VAR modélisant des tableaux à une dimension. Le code B de cette machine est présenté dans l’annexe E. Elle prend deux paramètres INDEX et VALUE correspondant respectivement à l’ensemble des valeurs permettant d’indexer le tableau et à l’ensemble des valeurs possibles pour les éléments du tableau. BASIC_ARRAY_VAR contient une variable arr_vrb et deux opérations prédéfinies:
L’opération VAL_ARRAY permet la lecture d’un élément du tableau, sa syntaxe est la suivante: vv VAL_ARRAY(ii). Sa précondition est que ii doit être un INDEX. Cette opération retourne l’élément en position ii.
REFINEMENT istacks_fun(ELEM) REFINES istacks
VARIABLES itop, next, empty INVARIANT
itop2ELEM ^ next2ELEM!ELEM ^ empty2BOOL
^ (empty = bool(istack = []))
^ (istack6=[] =) itop = last(istack))
^ (8ii.(ii22::size(istack) =) next(istack(ii)) = istack(ii-1)))
^ (istack6=[] =) next(first(istack)) = first(istack))
INITIALISATION
itop:2ELEMknext:2ELEM!ELEMkempty := TRUE
OPERATIONS push(ee) =
BEGIN
IF empty = TRUE THEN
next(ee) := eekitop := eekempty := FALSE
ELSE
next(ee) := itopkitop := ee
END END; pop =
BEGIN
IF itop6=next(itop) THEN
itop := next(itop) ELSE empty := TRUE END END END
Figure 5.4: Machine istacks_fun en B
L’opération STR_ARRAY permet l’écriture d’un élément du tableau, sa syntaxe est: STR_ARRAY(ii, vv) avec comme préconditions queiidoit être un INDEX et vv doit être une VALUE. L’application de cette opération mène à l’introduction de la valeur vv dans le tableau à l’index ii.
Afin d’implémenter la machine istacks_seq, nous avons importé la machine prédéfinieBASIC_ARRAY_VAR
en la préfixant à l’aide de deux noms différents tab et init. Ceci va nous permettre d’introduire deux tableaux. Le premier sert à raffiner la fonction next de la machine istacks_seq, il possède ELEM comme dimension et prend aussi ses valeurs dans ELEM. Donc next n’est autre que la variable prédéfinie arr_vrb. Le second est introduit ici afin d’initialiser la variable itop. Il ne contient qu’un seul élément et prend ses valeurs dans ELEM.
IMPORTS
tab.BASIC_ARRAY_VAR(ELEM,ELEM) , init.BASIC_ARRAY_VAR(1..1,ELEM)
CONCRETE_VARIABLES itop, empty INVARIANT
REFINEMENT istacks_seq(ELEM) REFINES istacks_fun
VARIABLES itop, next, empty INITIALISATION
itop:2ELEM ; next:2ELEM!ELEM ; empty := TRUE
OPERATIONS push(ee) =
BEGIN
IF empty = TRUE THEN
next(ee) := ee ; itop := ee ; empty:= FALSE ELSE next(ee) := itop ; itop := ee END END ; pop = BEGIN
IF itop6=next(itop) THEN
itop := next(itop) ELSE empty := TRUE END END END
Figure 5.5: Machine istacks_seq en B
INITIALISATION empty := TRUE
; itop <-- init.VAL_ARRAY(1)
Ici, l’opération push possède la même structure que celle de istacks_seq. L’écriture des valeurs ee et itop à l’indice ee est effectuée respectivement à l’aide des opérations tab.STR_ARRAY(ee, ee) et tab.STR_ARRAY(ee, itop).
push(ee) = BEGIN
IF empty = TRUE THEN
tab.STR_ARRAY(ee, ee) ; itop := ee
; empty := FALSE
ELSE
tab.STR_ARRAY(ee,itop) ; itop := ee
END END
Dans l’opération pop, nous déclarons une variable locale nn correspondant à la valeur présente à l’indice itop du tableau. Le test et la mise à jour sont effectués en prenant en compte cette variable.
pop =
VAR nn IN
nn <-- tab.VAL_ARRAY(itop)
; IF itop 6= nn THEN itop := nn ELSE empty := TRUE END END
Le code détaillé de la machine istacks_implementation est illustré dans la figure 5.6.
IMPLEMENTATION istacks_implementation(ELEM) REFINES istacks_seq
IMPORTS tab.BASIC_ARRAY_VAR(ELEM, ELEM) , init.BASIC_ARRAY_VAR(1::1, ELEM)
CONCRETE_VARIABLES itop, empty INVARIANT tab.arr_vrb = next INITIALISATION
empty := TRUE ; itop init.VAL_ARRAY(1)
OPERATIONS push(ee) =
BEGIN
IF empty = TRUE THEN
tab.STR_ARRAY(ee, ee) ; itop := ee ; empty := FALSE ELSE tab.STR_ARRAY(ee, itop) ; itop := ee END END; pop = VAR nn IN nn tab.VAL_ARRAY(itop) ; IF itop6=nn THEN itop := nn ELSE empty := TRUE END END END
Figure 5.6: Machine istacks_implementation en B0
La section suivante présente le langage B0_Uppaal avec ses caractéristiques et les règles à respecter. Il définit un sous-ensemble de B traduisible en automates Uppaal.