Vous êtes sur la page 1sur 79

Programmation Synchrone

Systèmes Temps Réel


Le langage Lustre

Ouassila Labbani-Narsis
2 ouassila.labbani@u-bourgogne.fr
Introduction - Historique
Lustre est un langage de spécification particulièrement
adapté pour la conception des systèmes de contrôle-
commande temps réel
⚫ Début de sa conception en 1984 dans le Laboratoire
de recherche VERIMAG à Grenoble (P. Caspi & N. Halbwachs)
⚫ Langage de programmation formel
⚫ But : simplifier et automatiser l’implémentation de
lois de commandes
⚫ Origine : notation graphique permettant la
représentation de circuits logiques en automatique
⚫ Coopération : informaticiens et automaticiens ont
mis la main à la pâte pour formaliser le tout...
2
Approche flot de données
⚫ Lustre travaille sur des flots de données
⚫ Si X est une variable Lustre, alors elle désigne le flot
X =(x1,x2,...), xn étant la valeur de x à l’instant n
Exemple :

3
Approche flot de données
L’approche flot de données consiste à représenter un
programme par un graphe où les noeuds sont des
opérateurs qui transforment des flots de données
Exemple:

 Interprétation synchrone :

4
Avantages de l’approche flot de
données
1. Approche fonctionnelle qui possède toutes les
propriétés mathématiques associés et en
particulier :
 L’absence d’effets de bord
➔ absence d’ordre dans l’écriture des instructions
➔ cela facilite la vérification, la réutilisation et la
transformation de programmes

2. Approche à parallélisme vrai où les contraintes de


séquencement et de synchronisation ne proviennent
que des flots de données, i.e ce sont les flots de
données qui déterminent les séquences
d’exécution et de synchronisation. Cela facilite la
synthèse de programmes parallèles
5
Autre version de l’exemple

⚫ Une définition pour chaque sortie et variable auxiliaire


⚫ Ordre indifférent
⚫ Principe de substitution
⚫ X,Y,M,S dénotent des séquences infinies de valeurs
6
Approche algébrique (déclarative)
⚫ LUSTRE est un langage formel suivant une
approche algébrique
➔ On parle aussi d’approche déclarative
⚫ Contrairement à un programme C, un
programme LUSTRE consiste en un ensemble
d’équations algébriques définissant les relations
entre flots d’entrées et de sorties

7
Système d’équations
déterministes
La résolution d’un système d’équations en
mathématique aboutit à trois types de solutions
possibles :

8
Système d’équations
déterministes
⚫ Afin d’ écrire uniquement des systèmes déterministes
(une seule solution...), Lustre n’accepte que les
systèmes d’équations de la forme :

Circularité non autorisée !

⚫ Les équations n’ont pas d’ordre :

9
Système d’équations
déterministes
⚫ Une équation définit une égalité mathématique, pas
une affectation : un flot peut être remplacé par sa
définition dans toutes les équations du nœud

⚫ Une équation définit un flot interne ou de sotie en


fonction de flots internes, d'entrée ou de sortie

signifie :
pour tout n 0, Xn = Yn + Zn et Zn = Un
10
Programme Lustre
⚫ Un programme Lustre définit un réseau de
composants travaillant sur des flots. En Lustre il
est possible de définir ses propres composants, on les
appelle nœuds
⚫ Un programme Lustre consiste en un noeud principal
et éventuellement d’autres noeuds auxiliaires
⚫ On appelle prototype du noeud la déclaration des
flots d’entrées et de sorties du noeud. Ces flots
doivent être typés
⚫ Des flots internes peuvent être définis
⚫ Le corps d’un noeud contient les équations
définissant les flots de sorties et internes en fonction
des flots d’entrées
11
Programme Lustre
Schéma représentant un programme Lustre

12
Structure d’un programme Lustre

13
Exemple d’un programme Lustre

14
Les types de données
Les types de base
⚫ booléen (bool), entier (int), flottant (real)

Les constantes
⚫ 2 ≡ 2, 2, 2, 2, ...
⚫ 1.0 ≡ 1.0, 1.0, 1.0, 1.0, …
⚫ true ≡ true, true, true, true, ...
Les tableaux
⚫ int^ 3, (real^ 5)^ 2, [int, bool, [int,real]] , ...

Remarque :
En vue de tenir l’hypothèse de synchronisme fort le
langage ne contient aucune structure qui pourrait
rendre non borné le temps de calcul (fonctions
récursives, types récursifs, les boucles, etc...) 15
Les opérateurs classiques
Les opérateurs “point à point”
⚫ Opérateurs arithmétiques et logiques classiques
X ≡ x0, x1, x2, x3 , … Y ≡ y0, y1, y2, y3 , …
 X + Y ≡ x0 + y0 , x1 + y1 , x2 + y2 , x3 + y3 , …
Les opérateurs arithmétiques
⚫ Binaire : +, -, *, div, mod, /
⚫ Unaire : -
Les opérateurs logiques
⚫ Binaire : or, xor, and, =>
⚫ Unaire : not

Les opérateurs de comparaison


⚫ =, <>, <, >, <=, >=
16
Exemple booléen
node Nand(X,Y: bool) returns (Z: bool);
var U: bool;
let
U = X and Y;
Z = not U;
tel

Exécution :

17
L’opérateur if-then-else
⚫ L’opérateur de contrôle : if-then-else peut se
traduire par “quand-alors-sinon”

Exemple :

Erreur classique :

Déterminisme : systématiquement then et else 18


Sémantique de if-then-else

19
Contraintes logicielles
⚫ Temps-réel : par hypothèse synchrone le
système réagit suffisamment rapidement

⚫ Prouver la validité de l'approche synchrone

⚫ Déterminisme : a chaque instant, chaque


flot possède une et une seule définition

⚫ Ressources bornées : la profondeur


d'imbrication de l’opérateur pre est bornée
(programme LUSTRE fini)
20
Exercice : Addition et
Soustraction de flot d’entiers

21
Correction : Addition et
Soustraction de flot d’entiers

22
Un autre exercice …
Écrire un programme Lustre qui prend en entrée
un flot réel E, en sortie deux flots : un flot de
sortie booléen B qui devient vrai quand l’entrée
dépasse 100 et un flot de sortie réel S qui renvoie
0 quand B est faux et qui renvoie la différence E -
100 quand B est vrai

Le prototype du noeud est le suivant :


node diff(E:real)returns(B:bool;S:real);

A vous de jouer !!!

23
Correction …

node diff(E:real)returns(B:bool;S:real);
let
B = E>100.0 ;
S = if B then 0.0 else E-100.0 ;
tel

24
Les flots de données et les
horloges

25
Les flots de données et les
horloges

⚫ Position dans la suite de valeurs du flot correspond à


la notion intuitive d’instant

⚫ Sémantique intuitive d’une horloge : un flot


possédant une horloge T est défini aux instants où
son horloge T est vraie

⚫ Pour l’horloge de base le flot est toujours défini

26
Les flots de données et les
horloges
Remarques :
⚫ Une horloge est un flot  elle possède une horloge
et ainsi de suite... jusqu’à l’horloge de base
⚫ Tout flot booléen définit une nouvelle horloge
➔ La suite des instants où le flot vaut true

⚫ D'autres horloges peuvent être définie par sous-


échantillonnage (opérateur when)

⚫ L'ensemble des horloges forme un arbre dont la


racine est l'horloge de base

⚫ Par commodité, l'horloge de base est notée true


27
Opérateurs temporels
⚫ pre (précédent) : opérateur permettant de
travailler sur le passé d’un flot
⚫ −> (suivi de) : opérateur permettant
l’initialisation d’un flot
⚫ when (sous-échantillonnage) : opérateur
permettant de transformer un flot continu en un
flot semi-continu
⚫ current (sur-échantillonnage) : opérateur
permettant de transformer un flot semi-continu
en un flot continu
28
Notation : nil l’indéterminé
et - l’indéfini
X = (nil, x1, x2,−,−, x3, . . .)
nil représente potentiellement n’importe quelle
valeur, il représente la seule indétermination qui
peut apparaître en Lustre, mais celle-ci ne se
manifeste (éventuellement) qu’à l’initialisation du
système

A différencier de : - la valeur indéfinie qui désigne


une valeur inexistante d’un flot à un instant
donné...

Attention : nil n’est pas un symbole du langage


Lustre, c’est seulement une notation de ce cours...
29
Mémoire
Opérateur pre (“précédent”)
⚫ Retard élémentaire
X x0 x1 x2 x3 x4 …
pre X nil x0 x1 x2 x3 …

(pre X)0 indéfini et i≠0 (preX)i = xi−1

Opérateur -> (“suivi de”)


⚫ Initialisation
X x0 x1 x2 x3 x4 …
Y y0 y1 y2 y3 y4 …
X -> Y x0 y1 y2 y3 y4 …

(X -> Y)0 = X0 et i≠0 (X -> Y)i = yi


30
L’opérateur pre (“précédent”)
retour vers le passé...
Permet de mémoriser la valeur précédente
d’un flot ou un ensemble de flots

⚫ Soit X le flot (X1,...,Xn,...) alors :


 pre(X) est le flot (nil ,X1,...,Xn,...)
 pre(pre(X)) est le flot (nil,nil,X1,...,Xn,...)
 ...

⚫ Par extension, l’équation (Y,Y’)=pre(X,X’) signifie :


 Y0 = nil , Y’0 = nil
 pour tout n > 0, Yn = Xn−1 et Y’n = X’n−1
31
L’opérateur −> : suivi de
Il permet d’initialiser un flot ou un ensemble
de flots

⚫ Soit X le flot (X1,...,Xn,...) et Y le flot


(Y1,Y2,...,Yn,...) alors :
 X −> Y est le flot (X1,Y2,...,Yn,...)

⚫ Par extension, l’équation (Z,Z’)= (X,X’)−>(Y,Y’)


signifie :
 Z1 = X1, Z’1 = X’1
 pour tout n > 1, Zn = Yn et Z’n = Y’n

32
Exemple1 : Détection des fronts
montants

Exécution :

33
Exemple2 : Minimum et
maximum d’une séquence

Exécution :

➔ Définition récursif du flot


34
Définitions récursives
Définition correcte :
⚫ La séquence peut être calculée pas-à-pas
➔ La récursion ne porte que sur le passé (pas de court-circuit)
Exemple : alt = false -> not pre alt
faux vrai faux vrai faux · · ·

Définitions incorrectes :
⚫ X = 1 / (2 - X);
⚫ Il y a bien une unique solution : X = 1
⚫ MAIS pas constructible pas-à-pas
⚫ Refusée par le(s) compilateur(s)

Règle à retenir : pas de court-circuit en Lustre


35
Modularité
Réutilisation
⚫ Tout noeud défini par l’utilisateur peut être réutilisé
⚫ Instanciation dans un style fonctionnel

Exemple :

36
Échantillonnage : opérateur when

Définir un flot “plus lent” que les entrées


➔ Sous-échantillonnage
X 4 -1 3 0 2 7 8
C vrai faux faux vrai vrai faux vrai
X when C 4 - - 0 2 - 8

⚫ Quand C est faux, X when C n’existe pas


⚫ On ne peut opérer que sur des flots de même
horloge
⚫ Exemple : “X + (X when C)” interdit !
37
Échantillonnage : opérateur when

Exemple :
⚫ Soit B un flot booléen, soit X un flot de type T

⚫ L’équation Y = X when B définit un flot semi-


continu Y de type T when B égal à X quand B
est vrai, indéfinie sinon

38
Projection : opérateur current

Ramène un flot sur une horloge “plus rapide”


➔ Sur-échantillonnage

X 4 -1 3 0 2 7 8
C vrai faux faux vrai vrai faux vrai
Y = X when C 4 - - 0 2 - 8
Z = current(Y) 4 4 4 0 2 2 8

current(X when C) ≠ X

39
Projection : opérateur current
Exemple :
⚫ Soit X un flot semi-continu de type T when B,
⚫ Soit Y un flot de type T
⚫ L’équation Y = current X définit un flot Y de
type T égale à X quand X est définie, égale à la
dernière valeur définie de X quand X est indéfinie, ou
nil s’il n’y a pas de dernière valeur

40
Sous-échantillonnage
⚫ On peut sous-échantillonner un flot déjà échantillonné
⚫ X when C correct  X et C ont la même horloge
⚫ current projette sur l’horloge immédiatement plus
rapide

41
Noeuds et horloges
⚫ Horloge effective d’une instance de noeud =
l’horloge de ses paramètres effectifs d’entrée

⚫ Échantillonner les entrées d’un nœud  forcer


tout “l’intérieur” du noeud appelé à fonctionner
plus lentement que le noeud appelant

Échantillonner les entrées ≠ Échantillonner les sorties

42
Calcul d'horloges
⚫ L'exécution d'un programme Lustre est une
évaluation paresseuse

⚫ Il faut enrichir les flots de sortie au fur et à mesure


que des valeurs sont ajoutées en entrée

⚫ Cela doit se faire en temps de réaction et en


mémoire bornée

⚫ Certaines expressions doivent donc être interdites :


Exemple : X + (X when C)

⚫ C'est le rôle du calcul d'horloges

43
Calcul d'horloges
But :
Attribuer statiquement à chaque flot une horloge
unique

⚫ Un flot qui n'est jamais échantillonné a pour horloge


true
⚫ Un flot échantillonné à pour horloge le second
argument du when
⚫ Les arguments d'un opérateur doivent avoir la
même horloge
⚫ Il n'est pas nécessaire de fournir au current
l'horloge de sur-échantillonnage car le compilateur
la calcule lui-même
44
Cohérence des horloges
⚫ Pour un noeud, l’horloge la plus rapide est l’horloge de
base
⚫ Les paramètres d’entrée sont sur l’horloge de base
⚫ Les constantes sont sur l’horloge de base
⚫ On ne peut opérer que sur des flots qui ont la même
horloge
⚫ L’horloge de X when C est C
⚫ L’horloge de current X est l’horloge de l’horloge de X
⚫ L’horloge de X op Y est l’horloge de X = l’horloge de Y

Problème : décider de la cohérence des horloges ?


45
Exemple de noeud “multi-
horloges”
node MultiHorloge(X, Y : int; C : bool; horloge de base
(Z : int) when C horloge d’interface)
returns (S : int) when C horloge d’interface);

var (H : bool) when C;


(U : int) when H;
let
H = (true when C) -> ((X + Y) when C) < Z;
U = (Z when H) -> Moyenne(Z when H, pre U);
S = current(U);
tel

node Moyenne(…)

46
Exemple de noeud “multi-
horloges”
node MultiHorloge(X, Y : int; C : bool; horloge de base
(Z : int) when C horloge d’interface)
returns (S : int) when C horloge d’interface);

var (H : bool) when C;


(U : int) when H; Horloge de base Horloge C
let
H = (true when C) -> ((X + Y) when C) < Z;
U = (Z when H) -> Moyenne(Z when H, pre U);
S = current(U);
tel Horloge C Horloge H
Horloge C Horloge H
node Moyenne(…)

47
Les tableaux

⚫ Types tableaux
 bool^4
 int^n avec n constante
 (real^4)^8
 ...

Mais …
un programme Lustre doit s'exécuter en temps
et espace borné. Les dimensions et indices des
tableaux doivent donc être statiques et connus à
la compilation
48
Les tableaux

⚫ Exemple :
node Tdelay (const d:int; x:bool) returns (y:bool);
var A : bool^(d+1);
let
A[0] = X;
A[1..d] = false^d -> pre(A[0..d-1]);
Y = A[d];
tel

node Main (A:bool) returns (A_delayed:bool);


let
A_delayed = Tdelay(10,A);
tel
49
Exemple d’un programme Lustre
node MUX (M:int) returns (C:bool; Y:int);
var (X:int) when C;
let
Y = if C then current (X) else pre (Y) - 1;
C = true -> (pre (Y) = 0);
X = M when C;
tel

50
Exemple d’un programme Lustre
node MUX (M:int) returns (C:bool; Y:int);
var (X:int) when C;
let
Y = if C then current (X) else pre (Y) - 1;
C = true -> (pre (Y) = 0);
X = M when C;
tel
Exemple d’exécution du nœud MUX :

51
Exercice 1 …
Le but est de réaliser un noeud qui cumule les
données d’entrées ...
Soit X un flot d’entrées entier
Soit Y un flot de sorties entier

(Y1,Y2,Y3,...) = (X1,X1+X2,X1+X2+X3,...)

Prototype du noeud :
node CumSum(X :int) returns (Y :int) ;

A vous de jouer !!!


52
Correction …
Le but est de réaliser un noeud qui cumule les
données d’entrées ...
Soit X un flot d’entrées entier
Soit Y un flot de sorties entier

(Y1,Y2,Y3,...) = (X1,X1+X2,X1+X2+X3,...)

node CumSum(X :int) returns(Y :int);


let
Y = X −> (X + pre(Y)) ;
tel
53
Exercice 2 : un compteur
⚫ Soit RAS (remise à zéro) un flot d’entrées booléen
⚫ Soit N un flot de sorties entier
⚫ A chaque instant le flot de sortie N s’incrémente
de 1, quand RAS est vrai N revient à zéro

A vous de jouer !!!

54
Correction : un compteur
⚫ Soit RAS (remise à zéro) un flot d’entrées booléen
⚫ Soit N un flot de sorties entier
⚫ A chaque instant le flot de sortie N s’incrémente
de 1, quand RAS est vrai N revient à zéro

node compteur(RAZ :bool)returns(N :int);


let
N = 0 −> if RAZ then 0 else (pre(N)+1);
tel

55
Utilisation du noeud Compteur
node dec(RAZ :bool) returns(N :int) ;
let
N = -compteur(RAZ) ;
tel
node IncDec(RAZ,ID :bool) returns(N :int) ;
let
N = if ID then compteur(RAZ) else dec(RAZ);
tel

56
Exemple d’un compteur
intermitant : when et current
Soit RAZ et B deux flots d’entrées booléens, N un flot
de sorties entier. Quand B est vrai le compteur
incrémente la sortie N
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

57
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

58
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

59
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

60
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

61
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

62
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

63
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

64
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

65
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

66
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

67
Exemple d’un compteur
intermitant : when et current
Exécution :
node compteurCond(RAZ,B :bool)returns(N :int);
let
N = current(compteur(RAZ when B)) ;
tel

68
Remarque …

Remarque suite à l’exemple précédent :

⚫ Un noeud existe seulement lorsque ses flots


d’entrées sont définis
➔ Le prototype du noeud doit obligatoirement
définir au moins un flot d’entrée ! Et ceci
même si le flot d’entrée n’est pas utilisé..

69
Erreur classique :
défaut d’initialisation

70
Remarques
⚫ f(x when c) ≠ f (x) when c
⚫ current (x when c) ≠ x
Exemple :
node somme(i:int) returns (s:int);
let
s = i -> pre s + i
tel

71
Autres exemples …
⚫ Détection de fronts montants
 Soit X un flot d'entrée booléen
 Soit Y un flot de sortie booléen
node EDGE (X : bool) returns (Y : bool) ;
let
Y = X -> (X and not pre(X));
tel
pre
→ Y

X Horloge

Y 72
Autres exemples …
⚫ Détection de fronts descendants
 Réutilisation du nœud EDGE
node Falling_EDGE (X : bool) returns (Y : bool) ;
let
Y = EDGE(not X);
tel X EDGE Y

X Horloge

Not X Horloge

Y Horloge
73
Autres exemples …
⚫ Un « switch »
 Soient set et reset des flots d'entrée booléen
 Soit level un flot de sortie booléen
node SWITCH (reset,set : bool) returns (level : bool);
let
level = true -> if set and not pre(level)
then true else if reset then false
else pre(level);
tel P1

reset
false
Exemple : Un bouton-poussoir
BOUTON = → level
true
SWITCH (change, change);
pre true

set
74
Mémo Récapitulatif

75
Mémo Récapitulatif

76
Mémo : Utilisation de Lustre
Un programme Lustre possède l’extension .lus. Nous noterons
prog pour désigner le nom d’un programme et NOEUD pour
désigner le nom d’un noeud définit dans ce même programme

Simulation d’un noeud :


Lustre dispose d’une interface de simulation (Luciole), basé sur
un interpréteur de programmes, permettant de visualiser
l’évolution des flots de données d’un noeud
luciole prog.lus NOEUD
Compilation d’un noeud :
La commande lustre permet d’obtenir un fichier NOEUD.oc. Il
s’agit d’un code objet écrit dans un formalisme OC commun à
Esterel et Lustre. Il s’agit d’une description du code Lustre sous
forme d’automates non nécessairement minimal
lustre prog.lus NOEUD
77
Mémo : Utilisation de Lustre
Génération de code C à partir de .oc :

poc NOEUD.oc
ou
ec2c NOEUD.oc

Minimalisation de l’Automate :

Il est possible de rendre minimal l’automate généré  on


diminue son nombre d’états :
Ocmin NOEUD.oc –c

Le compilateur Lustre est capable de produire directement un


automate minimal en utilisant l’option demand

lustre prog.lus NOEUD -demand -v


78
Synthèse
Lustre est un langage :
⚫ Simple, déterministe, exécutable,
⚫ Garantie que la mémoire nécessaire à l'exécution est bornée
⚫ Garantie que le temps d'exécution (exécution d'un cycle) est
borné
⚫ Formel car reposant sur une sémantique mathématique (non
exposée en cours)
➔ Génération automatique de code qualifié (suppression
des tests sur la cible)
➔ Possibilité de vérification formelles
⚫ Industriel
➔ Diffusé par Esterel Technologie : environnement SCADE
Mais …
Réservé aux domaines où on peut appliquer l'hypothèse synchrone
➔ Limité à la programmation de logiciels implantés sur des
monoprocesseurs
79

Vous aimerez peut-être aussi