• Aucun résultat trouvé

Modélisation UML de systèmes mixtes, vérification formelle, et simulation

6. Transformation de modèles UML vers Maude

6.2. Transformation de l’aspect comportemental

En utilisant Maude, le comportement de tâche dominée par les données est défini comme une séquence de règles de réécriture. Chaque règle de réécriture correspond à une transition entre deux états qui déclenche l'exécution d'un CGA. Une règle de réécriture peut être conditionnée par la survenance de certains événements et / ou des conditions éventuellement. Ci-dessous, nous donnons des règles de réécriture pour l'exemple de la figure 4.7 (a). Supposons que la tâche A est associée au processeur cpu.

rl [start] : ***1 start(A)

< A : Dtask | hwname : cpu, state : ready, > =>

< A : Dtask | hwname : cpu1, state : run, action : readC, token : 5 > . crl [readCwait] : ***2

< A : Dtask | hwname : cpu, state : run, action : readC, token : n >

< cpu : CPU | LinkTo : bus, ContextSwitch: cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp >

< ch1 : Dchannel | hwname : bus1, source : A, target : B, available : x > < C : Dtask | hwname : cpu, state : s > =>

< A : task | hwname : cpu, state : wait, action : readC, token : n – x >

< cpu : CPU | LinkTo : bus, ContextSwitch : cont, GoIdle : idl,Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp + (float(cont) * pw) > < ch1 : Dchannel | hwname : bus1, source : A, target : B, available : 0 > < C : Dtask | hwname : cpu, state : s > wakeup(C) if (x < n) and (s == ready) .

crl [readCreadEV5] : ***3

< A : Dtask | hwname : cpu, state : run, action : readC, token : n > < ch1 : Dchannel | hwname : bus1, source : A, target : B, available : x > =>

< A : Dtask | hwname : cpu, state : run, action : waitEV5 >

< ch1 : Dchannel | hwname : bus1, source : A, target : B, available : x - n > if n < x or n == x .

crl [waitEV5] : ***4

< A : Dtask | hwname : cpu, state : run, action : waitEV5 >

< cpu : CPU | LinkTo : bus, ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp >

< ch : Cchannel | hwname : bus1, source : A, target : B, size : sz > < C : Dtask | hwname : cpu, state : s, action : act, token : n1 > =>

< A : Dtask | hwname : cpu, state : wait, action : waitEV5 >

< cpu : CPU | LinkTo : bus, ContextSwitch : cont, GoIdle : idl,Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp + (float(cont) * pw) >

< ch : Cchannel | hwname : bus1, source : A, target : B, size: sz > < C : Dtask | hwname : cpu, state : s, action : act, token : n > wakeup(C) if (sz == 0) and (s == wait) and (act == writeC) .

crl [EV5occurence] : ***5

< A : Dtask | hwname : cpu, state : run, action : waitEV5 >

< ch : Cchannel | hwname : bus1, source : A, target : B, size : sz > =>

< A : Dtask | hwname : cpu, state : run, action : computeDCT, Nombre : 1000, Type : integer > < ch : Cchannel | hwname : bus1, source : A, target : B, size : sz – 1 > if sz > 0 .

rl [computeDCT] : ***6

< A : Dtask | hwname : cpu, state : run, action : computeDCT, Nombre : 1000, Type : integer > < cpu : CPU | LinkTo : bus ,ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp >

=>

< A : Dtask | hwname : cpu, state : run, action : writeA, token : 5 >

< cpu : CPU | LinkTo : bus, ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp + (float(1000 * iop)* pw) > .

rl [AccessBus] :***7

< A: Dtask | hwname : cpu, state : run, action : writeA , token : n > < bus: BUS | Speed : sp, Power : pb, TPower: tpb, free : f >

=> if f == true then < A : Dtask | hwname : cpu, state : run, action : writeA , token : n> <bus : BUS | Speed : sp, Power : pb, TPower :tpb, free : false > AccessBus(bus) else

< A : Dtask | hwname : cpu, state : wait, action : writeA , token : n > < bus : BUS | Speed : sp, Power : pb, TPower : tpb, free : f > fi .

rl [writeA] : ***8 AccessBus(bus)

< A : Dtask | hwname : cpu, state : run, action : writeA , token : n > < ch2 : Dchannel | hwname : bus, source : A, target : D, available : x > < bus : BUS | Speed : sp, Power : pb, TPower : tpb, free : f >

=>

< A : Dtask | hwname : cpu, state : run, action : computeINV, Nombre : 200, Type : float > < ch2 : Dchannel | hwname : bus, source : A, target : D, available : x + n >

< bus : BUS | Speed : sp, Power : pb, TPower : tpb + (pb * (float(n) / sp)) , free : true > . crl [ReaccessBus] : ***9

< A: Dtask | hwname : cpu, state : wait, action : writeA , token : n > < bus: BUS | Speed : sp, Power : pb, TPower: tpb, free : f >

=>

< A: Dtask | hwname : cpu, state : run, action : writeA , token : n > < bus: BUS | Speed : sp, Power : pb, TPower: tpb, free : f > if f == true .

rl [computeINV] : ***10

< A : Dtask | hwname : cpu, state : run, action : computeINV, Nombre : 200, Type : float > < cpu : CPU | LinkTo : bus ,ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp >

=>

< A : Dtask | hwname : cpu, state : run, action : end >

< cpu : CPU | LinkTo : bus ,ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp + float(200 * fop) * pw > .

rl [endA] : ***11

< A : Dtask | hwname : cpu, state : run, action : end >

< cpu : CPU | LinkTo : bus ,ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp >

=>

< A : Dtask | hwname : cpu, state : idle >

< cpu : CPU | LinkTo : bus ,ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp + float(idle * pwd) > .

rl [wakeupC] : ***12 wakeup(C)

< C : task | hwname : cpu, state : ready, > =>

< C : task | hwname : cpu, state : run, action : Compute1 , Number : 1000, Type : float > .

La première règle force la tâche A à changer son état de ready à exécuter la première instruction à savoir readC (lire cinq jetons). Nous utilisons la méthode start pour spécifier la première tâche à exécuter par cpu.

La deuxième règle force la tâche A à attendre sur l'opération de lecture, si le nombre de jetons sur le canal d'entrée est inférieur au nombre de jetons lus par la tâche. Dans ce cas, cpu s'arrête et réveille la tâche suivante nommée C qui est prête en utilisant le message wakeup.

Au cours de ce changement de contexte, nous mettons à jour TPower. En général, quand une tâche est bloquée, le CPU réveille la prochaine tâche qui est prête à l’exécution (l'ordre d'exécution est extrait à partir d’association UML).

La troisième règle spécifie la lecture succèsante de readC. Ensuite la tâche A attend la survenance de l’événement ev5. Dans cet exemple, nous supposons que la lecture ne nécessite pas d'avoir un accès au bus. Toutefois, si le canal est associé à la RAM, alors nous devons spécifier l'accès au bus et la mise à jour de l’énergie consommée qui due à la lecture de RAM, les transferts sur le bus et le pont.

La règle 4 force cpu à bloquer A, commuter le contexte et réveiller la tâche C.

La règle 5 spécifie la survenance de l'événement ev5 qui permet à la tâche d’exécuter

computeDCT qui inclut 1000 opérations entières. La règle 6 spécifie l'exécution de l’action computeDCT et de la mise à jour de TPower.

La règle 7 permet à la tâche A d’accorder l'accès de bus pour écrire des données: si le bus est libre (free == true), alors l'accès est accordé, par ailleurs, la tâche A sera bloquée.

La règle 8 spécifie l’opération de l’écriture et la mise à jour correspondante de l’énergie. Depuis que les écritures ne sont pas bloquantes, nous n'avons aucune condition sur la taille maximale des FIFOs de données. La tâche libère le bus quand elle termine l'écriture. La règle 9 permet à une tâche bloquée à ré-accéder au bus partagé.

La règle 10 spécifie un calcul qui s’appelle computeINV incluant 200 opérations flottantes et la mise à jour d’énergie consommée.

La règle 11 spécifie l'achèvement de l'exécution de la tâche. Ici, la tâche A change son état en idle et la valeur de TPower est mise à jour.

La règle 12 spécifie le réveil de la tâche C.

Pour spécifier les comportements des tâches dominées par le contrôle, nous appliquons les mêmes principes que pour les tâches dominées par les données. Toutefois, au lieu de modéliser les actions de calcul et les données entrées / sorties, nous spécifions les états de la FSM et les événements d'entrée / sortie associés. Ci-dessous, nous donnons trois règles de réécriture pour l'exemple de la figure 4.7 (b).

rl [start] :

< A: Ctask | hwname: cpu, state: ready, FSMS: BEGIN > =>

< A: Ctask | hwname: cpu, state: run, FSMS: STATE0 > . rl [state0] :

< A: Ctask | hwname: cpu, state: run, FSMS: STATE0 > => < A: Ctask | hwname: cpu, state: run, FSMS: waitEV1 > .

Crl [waitonEV1] :

< A : Ctask | hwname : cpu, state : run, FSMS : waitEV1 >

< cpu : CPU | LinkTo : bus, ContextSwitch : cont, GoIdle : idl, Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp >

< ch3 : Cchannel | hwname : bus, source : A, target : B, size : sz > < D : Dtask | hwname : cpu, state : s, action : act >

=>

< A : Ctask | hwname : cpu, state : wait, FSMS : waitEV5 >

< cpu : CPU | LinkTo : bus, ContextSwitch : cont, GoIdle : idl,Iop : iop, Fop : fop, Power : pw, PowIdle : pwd, TPower : tp + (float(cont) * pw) >

< ch3 : Cchannel | hwname : bus, source : A, target : B, size: sz > < D : Dtask | hwname : cpu, state : s, action : act > wakeup(D) if (sz == 0) and (s == wait) and (act == waitEV5) .

On note que l’estimation de la consommation d'énergie est basée sur l'hypothèse que l’énergie est proportionnelle au temps (nombre de cycles). Ici, nous nous somme intéressés à la consommation dynamique depuis que la consommation statique est généralement constante. Nous observons également que les tâches dominées par les données consomment plus d’énergie que celles de tâches dominées par le contrôle. Un autre facteur qui influe sur la consommation d'énergie est la politique d'ordonnancement (nombre de commutations de contexte).