Sous-sections
Mots clés : conception synchrone,
sémantique synchrone, programmation synchrone, Signal,
transformation de programme .
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 :
- Le comportement des programmes synchrones progresse via
une suite infinie d'actions composées.
- Chaque action composée est constituée d'un nombre borné
d'actions élémentaires, pour les langages Esterel, Lustre
et Signal.
- À l'intérieur d'une action composée, les décisions
peuvent être prises sur la base de l' absence de
certains événements, comme il apparaît sur l'exemple des
trois instructions suivantes, issues respectivement de
Esterel, Lustre, et Signal :
|
present x else `stat' |
l'action stat est exécutée en l'absence du
signal x. |
| |
|
|
y = current x |
en
l'absence d'occurrence du signal x, |
| |
y prend la valeur de la dernière occurrence
de x. |
| |
|
|
y := x default z |
en
l'absence d'occurrence des signaux x et
z, |
| |
y est absent, |
| |
sinon en l'absence d'occurrence du signal
x, |
| |
y prend la valeur de z. |
- Lorsqu'elle est définie, la composition
parallèle de deux programmes s'exprime toujours par la
composition des couples d'actions composées qui leur sont
respectivement associées, elle-même obtenue par la
conjonction de leurs actions élémentaires respectives.
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».
Sémantique synchrone
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 [4,9].
Considérant :
- A, un ensemble de
variables,
- D, un domaine de
valeurs incluant les booléens,
,
n'appartenant pas à D,
une trace T
sur
A1
A est une fonction
T : N
A1
(D
{
}).
Pour tout
k
N, un événement sur A1 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
(T), dont la sous-suite des
événements non nuls est égale à celle de T et initiale dans F. La projection
(F) sur un sous-ensemble
A2
A1 d'un flot
F, défini sur A1, est le flot
(T) pour T trace des restrictions des événements
de F à A2.
Un processus P
sur A1 est alors
défini comme un ensemble de flots sur A1. L'union de l'ensemble des
processus sur
Ai
A est noté
A.
Étant donné P1
et P2 deux
processus définis respectivement sur des ensembles de
variables A1 et
A2, leur
composition, notée P1|
P2, est l'ensemble des flots
F définis sur
A1
A2 tels que
(F)
P1 et
(F)
P2. La
composition de deux processus P1 et P2 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 P1 et P2.
Soit
le processus
ayant comme seul élément la trace (unique) sur l'ensemble
vide de variables. On montre alors que
(
A,
,|) est un
monoïde commutatif (cette propriété rend possibles les
transformations de programmes mentionnées en 3.1.2) :
| (P1|
P2)| P3 =
P1|(P2|
P3) |
| P1|
P2 = P2|
P1 |
P|
= P |
|
|
|
|
De plus, pour tout
A1
A, les processus
définis par l'ensemble
vide de flots sur A1 sont
absorbants :
P|
=
|
|
|
|
|
Enfin, l'opérateur de composition est idempotent (ceci
autorise la réplication de processus) :
Par ailleurs, si P est un
processus sur A1
et Q un processus sur
A2 inclus dans
A1, on
a :
si et seulement si tout flot de la projection de P sur A2 est un flot de
Q (Q est moins contraint que P).
Langage Signal
Un programme Signal [8] 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, Xt la valeur F(t)(X) 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.