Précédent : Présentation générale et
objectifs Remonter : Projet EP-ATR, Environnement de programmation
Suivant : Grands domaines
d'application
Résumé : Nous définissons la sémantique fonctionnelle d'un programme synchrone comme un ensemble de suites de valuations des variables de ce programme dans un domaine de valeurs complété par une représentation de l'absence d'occurrence d'une variable. Les opérateurs du langage Signal décrivent des relations sur de telles suites. Le compilateur de Signal est un outil formel capable de synthétiser la synchronisation globale d'un programme et l'ordonnancement de ses calculs. De nombreuses phases de compilation s'expriment par des transformations de programmes définies par des homomorphismes de programmes Signal.
Les usages différents des termes synchrone et
asynchrone selon les contextes dans lesquels ils
apparaissent font qu'il nous semble nécessaire de préciser,
autant que possible, ce qui constitue l'essence même du paradigme
synchrone [BB91,BCLH93,Ber89,Hal93]. Les points suivants
apparaissent comme caractéristiques de l'approche
synchrone :
Pour ce qui concerne la spécification de programmes (ou de propriétés), la règle ci-dessus est clairement la bonne définition de la composition parallèle.
S'il s'agit également de programmation, la nécessité que cette définition soit compatible avec une sémantique opérationnelle complique largement la condition «lorsqu'elle est définie».
La sémantique fonctionnelle d'un programme Signal est décrite comme l'ensemble des suites admissibles de valuations des variables de ce programme dans un domaine de valeurs complété par la notation d'absence d'occurrence [6,10].
Considérant :
une trace T sur est une
fonction
.
Pour tout , un événement sur
est
une valuation T(k) : une trace est une suite
d'événements. On appelle événement nul l'événement dans lequel
chaque valeur est égale à
.
Pour toute trace T, il existe une trace F unique
appelée flot, notée flot(T),dont la sous-suite des
événements non nuls est égale à celle de T et initiale
dans F. La projection sur un
sous-ensemble
d'un flot F, défini
sur
,est le flot flot(T) pour T trace
des restrictions des événements de F à
.
Un processus P sur est alors défini
comme un ensemble de flots sur
.L'union de l'ensemble des
processus sur
est noté
.
Étant donné et
deux processus définis
respectivement sur des ensembles de variables
et
, leur composition, notée
, est
l'ensemble des flots F définis sur
tels que
et
.La composition de deux processus
et
est ainsi définie par l'ensemble de tous les
flots respectant, en particulier sur les variables communes,
l'ensemble des contraintes imposées respectivement par
et
.
Soit 1p le processus ayant comme seul élément la trace
(unique) sur l'ensemble vide de variables. On montre alors que
(,1p,|) est un monoïde commutatif
(cette propriété rend possibles les transformations de programmes
mentionnées en
) :
Par ailleurs, si P est un processus sur et
Q un processus sur
inclus dans
,
on a :
Un programme Signal [9] spécifie un
système temps réel au moyen d'un système d'équations dynamiques
sur des signaux. Les systèmes d'équations peuvent être
organisés de manière hiérarchique en sous-systèmes (ou
processus). Un signal est une suite de valeurs à laquelle
est associée une horloge, qui définit l'ensemble discret
des instants auxquels ces valeurs sont présentes (différentes de
). Les horloges ne sont pas nécessairement reliées entre
elles par des fréquences d'échantillonnage fixes : elles
peuvent avoir des occurrences dépendant de données locales ou
d'événements externes (comme des interruptions, par exemple).
Le langage Signal est construit sur un petit nombre de primitives, dont la sémantique est donnée en termes de processus tels que décrits ci-dessus. Les autres opérateurs de Signal sont définis en terme de ces primitives, et le langage complet fournit les constructions adéquates pour une programmation modulaire.
Pour un flot F, une variable X et un entier
t on note, lorsqu'il n'y a pas de confusion possible,
la valeur
portée par X en t
dans le flot F. On note par le même symbole une variable
ou une fonction dans les domaines syntaxique et sémantique. Dans
le tableau ci-dessous, on omet les événements nuls (qui,
rappelons-le, terminent les flots ayant un nombre fini
d'événements non nuls).
Le noyau de Signal se compose des primitives suivantes :
La composition de deux processus P | Q se traduit directement en la composition des ensembles de flots associés à chacun d'eux.
La restriction de visibilité de X, P / X est la projection de l'ensemble des flots associés à P sur l'ensemble des variables obtenu en enlevant X à celles de P.
Comme on peut le voir pour les primitives, chaque signal a sa
propre référence temporelle (son «horloge», ou ensemble des
instants où il est différent de ). Par exemple, les deux
premières primitives sont monocadencées : elles imposent que
tous les signaux impliqués aient la même horloge. En revanche,
dans la troisième et la quatrième primitives, les différents
signaux peuvent avoir des horloges différentes. L'horloge d'un
programme Signal est alors la borne supérieure de toutes
les horloges des différents signaux du programme (les instants du
programme sont les instants de l'un au moins de ces signaux).
Le compilateur de Signal consiste principalement en un système formel capable de raisonner sur les horloges des signaux, la logique, et les graphes de dépendance. En particulier, le calcul d'horloges [1] et le calcul de dépendances fournissent une synthèse de la synchronisation globale du programme à partir de la spécification des synchronisations locales (qui sont données par les équations Signal), ainsi qu'une synthèse de l'ordonnancement global des calculs spécifiés. Des contradictions et des inconsistances peuvent être détectées au cours de ces calculs.
On peut toujours ramener un programme P comportant des
variables locales à un programme, égal à P, de la forme
Q/A1/.../An où Q est une composition de processus
élémentaires sans restriction de visibilité (i.e., sans
R/A). Un principe général de transformation de
programmes que nous appliquons (dans un but de vérification, pour
aller vers la mise en oeuvre, pour calculer des abstractions de
comportement) est alors de définir des homomorphismes sur les programmes Signal, tels que Q est égal à
la composition de ses transformés par
. Grâce
aux propriétés du monoïde commutatif, la transformation qui à
Q associe cette composition est elle-même un homorphisme.
On sépare ainsi un programme en différentes parties sur
lesquelles seront alors appliqués des traitements
spécifiques.
Résumé : Le principe de transformation des programmes Signal permet de décomposer un programme en une partie décrivant le contrôle booléen et une partie contenant les calculs. Le contrôle lui-même définit un système dynamique qui peut être étudié sous plusieurs aspects à des fins de vérification et de synthèse : étude de l'ensemble des états admissibles (partie statique), pour laquelle une forme canonique arborescente utilisant des BDD a été définie; calcul dynamique s'appuyant sur la représentation équationnelle d'un automate.
En appliquant le principe de transformation, on décompose un programme P en une partie Q(P) contenant le contrôle booléen et une partie C(P) contenant les calculs non booléens, telles que P=Q(P) | C(P).
Toute propriété de sûreté, qui peut s'exprimer sous la forme
d'un programme Signal R, satisfaite par Q(P), ce
qui s'exprime sous la forme R | Q(P)=Q(P), est également
satisfaite par P, puisqu'il résulte de P=Q(P) |
C(P) que P=Q(P) | P (voir ).
À une équation purement booléenne I correspond Q(I)=I;
C(I) est alors l'élément neutre du monoïde.
D'une équation monocadencée, est extraite par Q la partie
synchronisation des signaux; on obtient par exemple pour x :=
y+z l'expression :
synchro{x,y,z} | x := y+z
(synchro{x,y,z} spécifie l'égalité des horloges de
x, y et z).
Une équation de la forme x := y when b, dans laquelle
x est non booléen, est décomposée en :
synchro{x,y when b} | x := y when b.
Une équation de la forme x := y default z, dans laquelle
x est non booléen, est décomposée en :
synchro{x,y default z} | x := y default z.
Cette interprétation permet donc d'extraire, de façon automatique, par Q(P), l'aspect système à événements discrets, du système hybride spécifié par le programme. En raison de l'opérateur de retard qui introduit des indices temporels différents, le système est dynamique.
L'étude de ces systèmes dynamiques repose sur l'utilisation de techniques algébriques sur les corps de Galois. Elle vise à exprimer les propriétés des systèmes dynamiques et à donner une solution algorithmique pour leur vérification et pour la synthèse de systèmes satisfaisant certaines spécifications.
Q(P) est défini sur trois valeurs : { vrai,
faux, absent }. La sémantique des opérateurs de Signal et
l'approche flot de données équationnelle conduisent naturellement
à un codage de Q(P) en équations polynomiales sur le corps
(ou
), vrai,
faux, absent étant représentés respectivement par
(+ est l'addition modulo 3,
est la
multiplication usuelle).
L'étude de la sémantique abstraite d'un programme Signal se ramène alors à l'étude des systèmes dynamiques de la forme :
Un tel système dynamique n'est qu'une forme particulière de système de transitions à espace d'états finis. C'est donc un modèle de système à événements discrets sur lequel il est possible de vérifier des propriétés ou bien de faire du contrôle [8].
L'étude d'un programme consiste alors en :
Différentes techniques ont été développées pour ces deux problèmes. Les contraintes statiques sont essentielles pour la compilation des programmes Signal, et des techniques très efficaces ont été développées pour cela. La partie dynamique demande plus de calculs, et est utilisée principalement pour la vérification de propriétés; une technique plus générale -- mais en retour moins efficace -- a été développée pour la prendre en compte.
Le calcul d'horloges statique est au coeur du compilateur Signal; il en détermine largement ses performances. Ce calcul s'appuie sur l'ordre partiel des horloges, qui correspond à l'inclusion des ensembles d'instants (une horloge pouvant être plus fréquente qu'une autre).
La situation suivante doit être considérée : H
est l'horloge d'un signal, par exemple un signal à valeurs
réelles X, et Kest l'ensemble des instants où
le signal X dépasse un seuil : K := when (X
> X_MAX) . Alors 1/ chaque instant de K est un
instant de H , et 2/ pour calculer le statut de
K, il faut d'abord connaître le statut de H .
Il y a donc à la fois le fait que K est moins fréquent
que H et qu'il existe une contrainte de causalité de
H vers K . Ceci est dénoté par H K . De tels sous-échantillonnages
successifs organisent les horloges en plusieurs arbres,
l'ensemble de ces arbres constituent la forêt d'horloges
du programme considéré. Si un seul arbre est obtenu, la
synchronisation du programme et son exécution s'en déduisent
aisément.
La forêt associée à un programme donné n'est pas unique, la question de l'équivalence de forêts d'horloges se pose donc. Une forme canonique de forêt a été définie [1]. Un algorithme efficace pour trouver cette forme canonique a été développé. Il repose sur des manipulations préservant l'équivalence, prenant en compte l'ordre des variables résultant de la causalité, et combinées à des techniques BDD (Binary Decision Diagrams introduits par Bryant en 1986 [Bry86]).
Le calcul dynamique s'appuie sur la représentation équationnelle d'un automate : les automates, leurs états, événements et trajectoires sont manipulés au travers des équations qui les représentent. Calculer des trajectoires d'états ou d'événements, des états atteignables, des projections de trajectoires, des états de deadlock, etc., s'effectue alors sur les coefficients des équations polynomiales. De manière similaire, des techniques de synthèse de contrôle ont été développées pour un système dynamique donné pour les différents types de spécifications proposés par Manna et Pnueli [MP92,MP95].
La manipulation d'équations dans est tout à
fait similaire à la manipulation d'équations booléennes. Une
variante de la technique BDD, appelée
TDD (Ternary Decision Diagrams), a été
développée pour réaliser ces calculs. Des expériences ont montré
que le système formel Sigali qui en résulte peut effectuer en un
temps raisonnable des preuves sur des automates comportant plus
d'un million d'états atteignables.