ÉCOLE NORMALE SPERIEURE DE LYON (E.N.S)
Mémoire de Master2 recherche en informatique IF
COMMUNICATING REGULAR PROCESSES (CRP)
réalisé parNoureddine HAMADOUCHE.
Noureddine.Hamadouche@enslyon.fr dirigé par:
Paul FEAUTRIER
professeur de l'université versailles (paris) membre de l'équipe compsys (lyon) Paul.Feautrier@enslyon.fr Jury:
Laboratoire Informatique Parallélisme (LIP)
(Projet Inria :Compsys) Juin 2006.
résume:
le langage CRP est une extension de C destinée à l'écriture de programmes de type
« traitement de signal », en particulier de flux vidéo pour systèmes embarqués. Par rapport au C standard , il comporte des processus , des canaux et des ports.
l'objectif à long terme du projet est d'utiliser un programme CRP comme spécification d'un accélérateur matériel réalisé à l'aide d'un FPGA (ou d'un ASIC..) .
un programme CRP ne peut pas être compilé par le compilateur C standard et encore moins exécuté. en particulier, les motsclefs process,inport,outport et channel ne sont pas reconnus par le compilateur et sont génant car il est impossible de valider un programme CRP avant de réaliser le circuit correspondant.
le sujet principal du stage est de réaliser un outil logiciel permettant de transformer un programme CRP en un programme C++ utilisant les threads et ayant le même
fonctionnement.
Pour vérifier le bon fonctionnement de cet outil logiciel, on sera amené à enrichir la base d'exemples du projet syntol, par exemple en important et en adaptant des benchmarks postés sur le web par des projets analogues (StreamIt..).
Introduction:
La conception d’un système qui traite des flots vidéos à haute performance est un domaine fort , conduit par des marches comme la TV de haut débit, l'animation 3D et l'image médicale,les langages de programmation de ce type d’applications ont été introduits dans le cadre de la programmation des systèmes réactifs et en temps réel. différents projets sont destinés à l'écriture des applications de flux vidéo tels que (SANDRA,StreamIt,..) pour systèmes embarqués.
Le projet SYNTOL qui est en cours de développement par l'équipe compsys, il est basé sur un langage de programmation destiné à l'écriture de ce type d'applications , qui est le CRP (communicating regular processus) .Le modèle de communication basé en CRP, c'est le « processus et le réseau des processus ». La syntaxe de CRP est la même que le langage C[1], avec d'autre nouveaux mots clefs « channel,process,inport,outport » qui ne sont pas définis par le langage C,par conséquent on ne peut pas compiler un programme CRP par un compilateur C standard.
Le but dans ce stage est d'implémenter un outil logiciel qui permet de définir ces nouveaux mots clefs (en respectant la sémantique de langage CRP) et de transformer un programme CRP (après l'analyse lexicale et syntaxique) à un programme C++ en utilisant les threads ayant le même fonctionnement , afin de compiler ce programme par le compilateur de C standard.
Dans ce travail on garantie aussi la synchronisation entre les threads pour accéder a l' espace mémoire partagée (canal) .
Par conséquent , l'outil logiciel réalisé permet d'écrire d'autres applications de flux vidéo , afin d'enrichir la base des exemples de projet syntol.
Dans ce rapport , on présente différents langages et modèles de communication de processus et réseau de processus,puis on présente le langage CRP et la conception de notre outil logiciel en détail, d'autres problèmes et travaux de recherche dans le domaine seront présentés aussi.
Plan de travail:
résumé...
Introduction...
I.Généralité...5
1. les systèmes réactifs...5
1.1 Déterministe et concurrence...5
• Deterministe...5
• Concurrence...5
2.modèle de programmation parallèle...6
• évaluation...6
II.Programmation par échange de message et le Standard MPI...7
1.Principe...7
1.1.Primitives de communications...7
1.2 canal...7
2.Le standard MPI (message passing Interface)...8
3. inconvénients de MPI...8
III. Modèle KPN (Kahn Process Network)...8
1.Définition...8
2.Composants de base...9
a.Processus...9
b.Canal...9
c.Primitives (envoie/reception)...9
3.Exemple...9
4.L'intérêt de KPN...10
5. Limites et inconvénients de KPN...10
IV.Conception...11
1.Conception de CRP (communicating Regular Process)... 11
1.1.Introduction...11
1.2.Définition...12
1.3.Composants de base...13
1.3.1.processus...13
1.3.2.canal...13
1.3.3.Ports...13
1.3.4.Bornitude de canal...13
1.3.5.exemple commenté...14
2.Conception de transformation...15
2.1.Conception de processus...15
2.2.Les primitives de Synchronisation...15
2.3.Un canal et sa taille...15
2.4 Inport/Outport ...16
V.Implémentations...17
1.une vue générale sur la transformation...17
2.Structure de Compilateur...17
3. une vue générale sur la syntaxe de transformation...18
3.1.process... .18
3.2.channel...18
3.3. inport/outport...19
3.4.boucle infini...19
3.5. lancement de processus...19
3.6.opérations de ré initialisation...20
4.contraints...20
5.exemple de transformation...21
VI.Application...24
1.la norme MPEG...24
2.structure d'une séquence video...24
3.Description de shéma de codage/stockage...25
• Description des processus...25
VII.Efficacités et Discussion...26
1. degré de parallélisme...26
2.syntax de langage...27
3. consommation de mémoire...27
IIX. Conclusion...29 Annexe
Bobliographie
I.GENERALITE :
1.systèmes Réactifs:On peut schématiquement classer les systèmes informatiques en trois grandes familles:transformationnels,interactifs et réactifs[8]. Les systèmes transformationnels disposent de leurs entrées dès leur initialisation et délivrent leur résultats lors de leur
terminaison. l'exemple idéal de cette classe est un solveur d'équations dont la seule contrainte est qu'il doit se finir en un temps fini.
Les systèmes interactifs réagissent continuellement avec leur environnement à leur propre vitesse. On trouve dans cette classe les systèmes d'interrogation de bases de données et les systèmes d'exploitations, dont on réclame généralement avant tout la sûreté de
fonctionnement.
La dernière famille de système,dit réactifs[8],réagissent continuellement à leur environnement et à la vitesse imposée par celuici.
Les systèmes temps réels,les applications de traitement de signal et les applications vidéos à haute performance ...etc , sont des bons exemples de tels systèmes.
De nombreux systèmes réactifs sont des systèmes embarqués,disposant de ressources réduites, il est donc également nécessaire de connaître les ressources nécessaires à l'exécution des programmes. Pour satisfaire ces deux caractéristiques,les outils de description de systèmes réactifs doivent garantir une exécution des programmes en temps et en mémoire bornée[8].
1.1 Déterminisme et concurrence
Les systèmes réactifs , de par leur nature et leur contexte d'utilisation,partagent deux caractéristiques: déterminisme et concurrence.
• Déterminisme
Les systèmes réactifs sont par nature déterministes : ils doivent réagir de la même
manière aux mêmes sollicitations de l'environnement. cette caractéristique les différencie des systèmes interactifs,qui sont souvent nondéterministes. De plus ,les activités de mise au point,de certification et de test sont plus simples dans le cas de programmes
déterministes. Cette caractéristique est ici particulièrement importante dans les systèmes embarqués.
• Concurrence
Ces systèmes sont concurrents,le système travaille en parallèle avec son environnement et interagit avec celuici .Il est plus souvent plus simple et plus naturel de pouvoir donner une description parallèle d'un système permettant à la fois d'obtenir des définitions plus
simples et plus proches des spécifications des systèmes[8].
D'après ces définitions ,et puisque les systèmes embarqués sont des systèmes réactifs,ils vérifient la caractéristique de parallélisme(concurrence),c'est à dire que les programmes de type « traitement de signal » par exemple sont des programmes parallèles.
Dans les paragraphes suivants nous présentons quelques langages et modèles de
programmations destinés à l'écriture de ce type d' applications ,aussi les différents modèles de communication.
2.Modèle de programmation parallèle
Ce modèle doit abstraire le plus possible les traits liés à l'organisation d'un programme parallèle, comme la décomposition ,l'ordonnancement,les communications et la synchronisation.
La décomposition est la façon par laquelle un programme parallèle est divisé en taches(modules) indépendantes.
L'ordonnancement est lié à l'attribution de ces taches aux processeurs.
Ces deux items sont particulièrement importants pour obtenir l'efficacité optimale d'exécution d'un programme parallèle.
La communication est une conséquence des dépendances de données entre deux taches ,et nécessite la détermination des points adéquats où sera réalisé l'échange de données .
Enfin la synchronisation permet de garantir que l'état global des taches reste cohérent c'est à dire que les propriétés invariantes d'état restent toujours vérifiées.
Une bonne abstraction de ces traits aide à la compréhension et au développement d'applications parallèles.
• évaluation
L'emploi du parallélisme rend possible un accroissement des performances dans différents domaines d'applications. Cependant l'expression de ce parallélisme et l'exploitation des facilités mises à disposition par les architectures parallèles restent toujours difficiles à l'heure actuelle.
Un programme parallèle reste complexe à élaborer car on doit coordonner les différentes entités qui coopèrent pour l'obtention de la solution. Cette
coordination se présente sur différents niveaux :
comment séparer un programme en taches indépendantes?
comment déterminer quand et par qui une tache sera exécutée?
comment échanger des données entre les taches?
Pour essayer de répondre à ces questions la recherche en programmation parallèle propose différents modèles et environnements de programmation et de
communication.
L'une des approches les plus intéressantes repose sur des modèles de programmation basés sur l'interaction entre les processus et le réseau des processus .Ces modèles privilégient une découpe du programme en processus et fournissent des mécanismes pour la communication .
Deux paradigmes de programmation largement répandus utilisent cette approche : la programmation par échange de message et la programmation par mémoire partagée.
dans ce qui va suivre nous présentons l'architecture de passagemessage (standard MPI),et le modèle de KPN(kahn process network).
II.Programmation par échange de message et le Standard MPI
1. Principe
Le parallélisme consiste essentiellement à diviser un problème donné en plusieurs sous problèmes et à les résoudre simultanément.
un programme parallèle est donc vu (dans ce modèle de programmation) comme un ensemble de processus coopérants.
Ces processus peuvent être exécutés simultanément sur différentes machines, ou concurremment sur une seule machine. étant donné qu'il n ' y a aucune restriction à propos du nombre de processus par processeur (machine parallèle virtuelle).
Les processus ne partagent pas de mémoire , la communication entre eux ne se fait que par l'émission et la réception de message et au travers d'un canal .
1.1 les primitives de communications:
Deux opérateurs de base send(),et recv() permettent à une paire de processus l'émetteur et le destinataire d'échanger des messages entre eux .le nombre et la
complexité des paramètres de ces primitives varient selon la bibliothèque d'échange de messages utilisée.
send (message,type,quantité,,étiquette,dest);
recv (message,type,quantité,,étiquette,source);
1.2 un canal :
Dans le plus simple des cas , il existe un canal unidirectionnel entre toute paire de processus. sur un canal , les messages sont acheminés dans l'ordre d'émission (un canal FIFO) . Du point de vue de l'émetteur ou du récepteur, ce canal est simplement identifié par le processus interlocuteur .C'est la solution qui s'oppose à celle identifiant
explicitement le canal (e.g. socket dans les protocoles IP). Si deux processus utilisent des flots de messages distincts , il faut pouvoir gérer plusieurs canaux logiques entre ces processus .Ceci est fait simplement en étiquetant les communications (tag) par le numéro de canal logique à utiliser entre 2 processus[12].
2.Le standard MPI (message passing Interface):
Le MPI est un standard issu des efforts de constructeurs de machines parallèles, d'utilisateurs , de laboratoires de recherche et d'universités, afin de définir la syntaxe et la sémantique d'un ensemble de routines destinées au développement de programmes parallèles basés sur le paradigme d'échange de message.
Il fournit des primitives pour le développement des programmes parallèles tels que:
les opérations d'accès à une mémoire distante ,les opérations de communication entre processus et des opérations d'entrée/sortie(certaines primitives en particulier relatives au temps réel sont fournies par la version MPI2.0)[12].
3. inconvénients de MPI
• Les applications qui utilisent cette bibliothèque sont non déterministes.
• L'analyse et le débuguage des programmes utilisant cette bibliothèque sont très difficiles.
Afin de remédier à l'inconvénient de nondéterminisme présent dans le standard MPI on présente un autre modèle de communication , c'est le KPN (Kahn Process Network).
III.KPN (Kahn Process Network)
Pour montrer le parallélisme et la distribution de l'application ,nous avons
présenté un autre modèle de réseau de processus , celui proposé par Gille Kahn (KPN).
ce modèle est souvent utilisé pour modéliser les applications de traitement de signal . 1.Définition
Un KPN est un modèle de réseau des processus composé d'un ensemble fini de processus qui communiquent entre eux par des canaux (file de type FIFO) disposant d'une capacité de stockage infini[3][6].
Chaque processus exécute un programme séquentiel pouvant utiliser des instructions de lecture ou d'écriture sur un canal spécifié , de plus un KPN respecte la règle suivante:
un canal ne peut être connecté qu'à un seul processus en entrée et un seul processus en sortie . Si le comportement des processus est déterministe, alors Gille Kahn a montré que
l'histoire de chaque canal est également déterministe[3] .
2.Composants de base:
a) processus: Un KPN est composé d'un ensemble de processus indépendants qui sont vérifiés , la propriété de déterminisme.
b) canal: On trouve en KPN un ensemble de canaux ,chaque canal présenté par une file FIFO de taille infinie.
c) Les opérations de lecture ou écriture (envoi ou réception) sont de type :
– bloquant pour la lecture (réception).
– non bloquant pour l'écriture (envoi) .
d) Chaque canal ne peut être connecté que par un producteur (un processus en entrée) et un consommateur (un processus en sortie)[3][4] .
produit consommer après la production
figure1. la lecture et l'écriture dans un canal avec KPN 3. un exemple:
processus three (int outport out) processus four (int inport in){
int j;
{ int k,t;
int i; for (j=0 ; ; j++) for (i=0 ; ; i++) { for (k=0; k<3;k++) w1: send (out,1); R: t[k]= receive (in);
}
} int channel c;
four (c);
three (c);
• Description d'exemple:
– La boucle infinie signifie que la taille de canal est infinie.
– L'opération de send permet d'écrire dans un canal.
– L'opération de receive permet de lire une donnée dans une case de canal.
– Dans la fonction principale l'interface entre un processus et un canal est réalisé au moment de lancement des processus.
| | | | | | | | | | | | | | | | | | | | | | |
4. L'intérêt de KPN
L'un des intérêts des KPN en synthèse de systèmes embarqués est qu'ils permettent de décrire des systèmes réactifs comportant du parallélisme, tout en se prêtant à une analyse
« par dépendances » analogue à celle qui permet de traiter les codes séquentiels.
Un autre intérêt est le comportement déterministe de canal, c'est le meilleur modèle après les programmes séquentiels déterministes[3].
D'autre part , ils conduisent à une représentation graphique qui est familière aux spécialistes du traitement du signal et d'électronique.
Plusieurs applications de traitement de signal ,spécification VLSI utilisent le KPN comme un modèle de communication.
5.Les limites et inconvénients de KPN
Le modèle de programmation KPN est asynchrone,par contre la plus part des systèmes embarqués ont un modèle d'exécution synchrone,parce que les systèmes synchrones sont faciles à spécifier ,implémenter et vérifier ,on peut aussi et facilement satisfaire les contraintes de temps réel avec les systèmes synchrones.
– L'un des problèmes les plus intéressants qui se pose au sujet de KPN est la bornitude des canaux ,qui conditionne évidemment la faisabilité du système .
– Un autre aspect est celui de degré de parallélisme ,qui pour un KPN , est de l'ordre du nombre de processus ( un KPN ,il n'accepte qu'un producteur et un consommateur dans un canal donné) , il peut être rare que ce degré puisse correspondre du premier coup au matériel utilisé pour l'implémentation. il peut être aussi bien trop élevé que trop faible,il n'existe donc pas de paradoxe pour parler de parallélisation d'un KPN.
– Une autre limite qu'on peut présenter en KPN, c'est ce qui concerne la contrainte de compte des messages. pour la construction d'une dépendance de message , on a besoin de compte des messages, c'est à dire on calcule le nombre des opérations
d'envoi/réception, sur un canal donné[4].
Note: Après la présentation de quelques concepts généraux qui sont liés à l'environnement de travail tels que le système réactif,système en temps réel,et quelques modèles de
communication comme le standard MPI, et le modèle de KPN, on passe maintenant à la présentation de la conception et l'implémentation de l' approche (CRP communicating Regular Process), qui est au coeur de notre travail dans ce stage.
IV. Conception
1.Conception de CRP (Communicating Regular Process)
1.1.Introduction:
Dans ce stage nous étudions un problème fondamental issu de l'expérience industrielle et académique: la conception de système de traitement de flux vidéo à haute
performance.
Les langages de programmations de ce type d'applications sont basés sur le modèle de communication de processus (réseau de processus) ,et différents projets sont destinés a l'écriture de ce type d'applications (e,g projet de SANDRA , Philips Eindhoven) qui écrit une implémentation à base de réseau de Kahn (KPN) .
Malheureusement le modèle de KPN a l'inconvénient d'être asynchrone et présente aussi le problème de comptage des messages (comme nous les avons précédemment présenté).
Afin de remédier à ces problèmes , et Dans le cadre de projet de SYNTOL ,
Paul Feautrier propose une approche[1], destinée à l'écriture des applications de flux vidéos, c'est le CRP (Communicating Regular Process) et le modèle de communication en CRP .
Un programme CRP est utilisé comme une spécification initiale (figure 2), dans le projet de syntol.
figure2. l'emplacement de CRP dans le projet syntol
Le langage CRP est une extension de C destinée à l'écriture des programmes de type
« traitement de signal » pour systèmes embarqués. Par rapport au C standard , il comporte des processus , des canaux et des ports[1] .
Un programme CRP ne peut pas être compilé par le compilateur C standard et encore moins exécuté. En particulier , les motsclefs: process,inport,outport et channel ne sont pas reconnus par le compilateur C. Ceci est gênant car il est impossible de valider un programme CRP avant de réaliser le circuit correspondant .
Le travail principale du stage est de réaliser un outils logiciel permettant de transformer un programme CRP en un programme C++, avec la définition des mots clefs (channel,process, inport,outport) utilisant des outils ayant le même fonctionnement.
Pour vérifier le bon fonctionnement de cet outil logiciel , on sera amené à enrichir la base d 'exemple du projet Syntol , par exemple en important et en adaptant des benchmarks postés sur le web par des projets analogues (SPARK, STREAMIT).
Dans ce chapitre nous présentons la sémantique de langage CRP et ces composants de base , puis nous présentons la conception de transformation d'un programme CRP en un programme C++.
1.2.Définition
Le CRP est un langage de spécification [1][5] qui utilise un processus comme un module (comme dans KPN),mais la sémantique de canal est changée .Ici on représente un canal par un tableau de dimension arbitraire, un seul processus a le droit d'écrire dans chaque canal , et cette écriture doit se faire en assignation unique : chaque case du canal ne doit être modifiée qu'une seule fois . par contre il n'y a pas de restriction sur les lectures. il n'y a donc pas d'ordres spéciaux pour l'émission ou la réception d'un message ( i,e une seule écriture et plusieurs lecture).
Figure 3: Communicating Regular Process (CRP)
1.3.Composants de base 1.3.1. Processus:
un processus est un :
Programme séquentiel , qui s'exécute en parallèle avec d'autres processus.
Un programme en CRP est composé de plusieurs processus.
Tous les processus d'une application démarrent ensemble au début de l'application .
Chaque processus a des variables locales indivisibles à d'autres processus (on ne peut pas changer le contenu d'une variable globale ) .
Un processus peut être activé plusieurs fois .
Les processus communiquent entre eux seulement à travers des canaux.
Un seul processus a le droit d'écrire dans un canal ,et plusieurs processus de lecture peuvent accéder à une case en même temps [1][5].
1.3.2 .canal:
Un canal est un moyen de communication entre les processus , considère comme un tableau de dimension infinie supportant une seule écriture / plusieurs lectures[1].
chaque cellule de canal contient un bit de contrôle qui indique si cette cellule est pleine ou vide .
L'opération de lecture ne produit aucune modification du contenu de cellule d'un canal.
1.3.3. Ports
Un port est l'interface entre un processus et un canal .un processus peut être activé plusieurs fois ,par un ordre de lancement situé dans un processus de plus haut niveau.
un processus accède à un canal à travers un port , qui peut être d'entrée (inport) ou de sortie (outport) . la correspondance entre port et canal se fait au lancement du processus .De cette façon, les diverses activations d'un même processus peuvent utiliser des canaux différents[1].
1.3.4. la bornitude de taille de canal
La définition de langage CRP dit que la taille de canal est infinie , il est nécessaire dans la plus part des systèmes embarqués d'utiliser une boucle infinie .mais au niveau du matériel il est impossible de satisfaire cette condition parce que la taille de la mémoire est limitée.
La solution est d'analyser la durée de vie de chaque donnée dans une case de canal et vider ou réinitialiser cette case après le dépassement de cette durée de vie ( après la dernière utilisation du contenu de cette case ), et dans ce cas le compilateur de CRP accepte la boucle infinie avec l'utilisation d'une file circulaire de type FIFO.
le seul problème qui peut être posé dans ce cas est quand l'application supporte beaucoup de parallélisme , qui exige un espace de mémoire important et qui peut être infini.
Pour présenter une vue générale sur les programmes en CRP on introduit l'exemple suivant :
1.3.5.Un exemple commenté
Le programme suit est composé de 2 processus producer, et consumer et la fonction principale main.
process producer (outport int x []) {
int i;
for (i=0 ; ; i++ ) K: x[i]=2*i;
}
void traitement (int);
process consumer (inport int y[]) {
int i;
int j;
for (i=0 ; ; i++ ) W: j=y[i];
Z: traitement (2*j);
}
process producer (outport sortie[]);
process consumer (inport entre[]);
void main () {
channel a[];
T1: consumer (a);
T2: consumer (a);
S: producer (a);
}
Ces 2 processus sont enregistrés dans le même fichier. ceci n'est pas obligatoire. il est plus fréquent d'attribuer un fichier distinct à chaque processus (notre simulation supporte le deuxième cas).
Le processus producer fabrique une valeur par une règle arbitraire (instruction K) et la range dans le tableau x. ce tableau est en fait le canal a, avec le processus consumer .Ce processus lit chaque élément du canal y (instruction W) ,et appeler a la fonction traitement.
Le processus consumer est lancé 2 fois avec des canaux différents comme il écrit dans la fonction main (instruction T1 et T2).
On peut considérer que l'instruction K est l'émission d'un message et que W est sa réception . Comme le processus consumer va avoir tendance à aller plus vite que le processus producer (il n'a qu'une instruction à exécuter par tour de boucle au lieu de 2). il faut assurer une
synchronisation.
Dans tous les processus, chaque opération de lecture ou d'écriture se trouve dans une boucle infinie.
Dans la fonction principale les instructions T1,T2,S présente le lancement de tous les processus ensemble au démarrage .(voir le code de ce programme en C++ en V.5).
2.Conception de transformation
Nous avons montré précédemment qu'on ne peut pas compiler un programme CRP par un compilateur de C standard a cause des mots clefs: inport outport, channel et
process .alors pour compiler un programme CRP on a besoin d'un véritable compilateur qui définit ces mots clefs .
Dans ce qui suit nous présentons la conception de transformation d'un programme CRP en un programme c++ et des outils qui permettent de définir ces mots clefs tout en gardant la même sémantique de chaque composant (channel , inport,outport , process) comme cela est présenté dans (1).
Nous présentons aussi notre conception qui fournit des outils de synchronisation pour accéder aux canaux , de telle sorte d'éviter qu'un processeur attende d'accéder à une case vide.
2.1. conception d'un processus
On transforme chaque processus en CRP en une fonction.
Le lancement de processus correspond dans notre conception a un lancement d'un thread qui exécute la fonction.
2.2. les primitives de synchronisation
Un canal est représenté par un tableau de dimension arbitraire,les canaux deviennent la mémoire partagée entre les threads .
Dans notre implémentation nous fournissons des primitives qui sont rassemblées sous forme d'une bibliothèque (voir la partie d'implémentation).et qui garantissent la
synchronisation entre ces threads , les deux primitives (consumer et producer) vérifient cette synchronisation avec les contraintes relatives a la sémantique d'un programme CRP développées dans les points suivants:
Un thread de lecture ne peut accéder à une case vide (ne contient aucune donnée).
Si un thread de lecture et un autre d'écriture accèdent en concurrent à la même case,on bloque le thread de lecture et on laisse le thread d'écriture passer (la figure 4).
Un seule thread d'écriture peut accéder a une case , et plusieurs threads de lecture peuvent accéder a la même case, comme c'est présenté dans la figure4.
La figure4 présente l'accès de plusieurs threads (soit en lecture ou en écriture) dans un canal.
thread d'écriture
lecture lecture thread de lecture bloquer
lecture
figure4: la sémantique de lecture et d'écriture dans un canal en CRP
2.3. un canal et sa taille
La taille de canal en CRP est non bornée, et dans notre conception nous utilisons un tableau de taille arbitraire pour définir un canal, ( on définit la taille de canal dans une variable globale ) . alors pour vérifier le condition de non bornée , nous fournissons une primitive qui permet de réinitialiser chaque case dans le canal après l'utilisation, la procédure de réinitialisation suit les étapes suivantes:
– Compte le nombre de processus qui lisent dans un canal donné .
– Lancer un compteur dès la première lecture .
– Réinitialiser la case lorsque le compteur atteint le nombre de processus qui doivent être lus dans ce canal.
2.4. inport et outport
L'interface entre un canal et un processus est établie soit en entrée par inport ou en sortie par outport (voir la partie d'implémentation pour le détail).
| | | | | | | | | | | | | | | | | |
V. Implémentation :
Dans les paragraphes précédents nous avons présenté , la conception des nouveaux mots clefs (channel,process,inport,outport) dans notre compilateur, qui permet de compiler un
programme CRP , et fournit des outils ayant le même fonctionnement, afin de respecter la sémantique et les contraintes d'un programme CRP. nous avons présenté aussi les primitives qui permettent de garantir la synchronisation entre les threads.
Dans ce qui va suivre nous présentons la partie d'implémentation de notre compilateur , et les différentes approches qui nous avons implémentées dans ce sujet telles que la bibliothèque qui fournit des primitives de synchronisation, la structure de compilateur, et la structure de canal en détail .
1.Une vue générale sur la transformation d'un programme CRP en programme C++.
Le travail principal dans la partie d'implémentation que nous avons réalisée consiste à analyser un programme CRP (analyseur lexical et syntaxique), à l'aide de yacc et lex, et à convertir son contenu en un programme C++ . Ce contenu écrit dans un nouveau fichier d'extension « .C » ,une fois la conversion est terminée on compile ce nouveau programme par un compilateur de C standard et on ajoute notre bibliothèque qui fournit des primitives de synchronisation , la figure 5 résume ce travail.
fichier.h compilateur programme de C standard en CRP programme +
en C++ bibliothèque fichier exécutable
figure 5: le déroulement de transformation d'un programme en CRP à l'aide de notre compilateur et bibliothèque.
2. la structure de compilateur
La figure 6 présente la structure générale de compilateur qui réalise cette transformation.
Le fichier cri.ml contient la représentation interne d'un programme CRP, les fichiers clex.mll et cstax.mly correspondant aux analyseurs lexical et syntaxique.
Le fichier xpretty.ml contient le code qui permet de transformer chaque instruction CRP en instruction en C++ , y compris les mots clefs processus,channel,inport, outport.[2]
notre compilateur
figure6: graphe de dépendance de compilateur
3.une vue générale sur la syntaxe de transformation
On a déjà dit que la syntaxe d'un programme CRP est la même qu'un programme C, il reste de transformer les mots clefs inport,outport,channels et processus.
3.1 process : Le mot process est définit comme un type de base .
Le convertisseur de type process indique que c'est un type d'une fonction ,par exemple process foo (paramètres,,,);
alors le corps de la fonction foo doit être lancé par un thread.
on transforme l'exemple précédent comme suivant: void foo (paramètre);
3.2 channel : La déclaration de mot clef channel en CRP joue le même rôle que : volatile ou const .
L' exemple suivant : channel int c ;signifie que la variable c est un canal de type entier.
Le convertisseur transforme cette écriture comme suit: canal< int > c ; c.Init(paramètres);
c'est à dire que c est un canal de type entier, et la deuxième instruction permet d'initialiser les paramètres de ce canal.
cri.ml
tools.ml
cstax.mly
clex.mll
xpretty.ml
cxml.ml
La classe canal contient:
Un tableau DATA de type template , la taille de ce tableau est constante (TAILLE_CANAL) .
Chaque champ de tableau comporte aussi 3 bits:
• un bit ecrit, et bit plein: de type sémaphore pour indiquer si la case est vide ou plein.
• un bit bit contient le nombre des processus qui doivent être lus dans ce canal.
Définit aussi les primitives producer, consumer qui permettent d'effectuer des
opérations de lecture et écriture dans un canal, tout en garantissant la synchronisation.
Une primitive de réinitialisation de case dans un canal.
3.3 inport et outport
La déclaration d'une variable de type inport ou outport comme dans l' exemple:
inport float a; signifie que la variable a est l'interface vers un canal de type float.
Le convertisseur transforme cette déclaration comme suit:
canal<float>*a; tel que a est un pointeur vers un canal de type float, et la même chose pour outport.
3.4 la boucle infinie:
La syntaxe de la boucle infinie utilisée dans un programme CRP écrit comme dans l'exemple suivant:
process source(outport float a[]){
int i;
for(i=0;;i++) P: a[i] = cos(i);
}
Le convertisseur transforme l'écriture de la boucle for comme suit:
for (i=0; i<TAILLE_CANAL;i=div(i+1,TAILLE_CANAL).rem) { ...}
tel que TAILLE_CANAL est constant .
L'instruction i=div(i+1,TAILLE_CANAL).rem; permet de toujours vérifier la condition
« i>TAILLE_CANAL » (i,e le compteur est toujours inférieur à la taille de canal).
3.5. Lancement d'un processus
L'instruction de lancement de processus dans le programme CRP est présentée par un appel a ce processus, comme c'est présenté dans l'exemple suivant:
process foo (inport float a);
void main () {channel k;
W: foo(k);}
Le convertisseur transforme l'instruction w à l'instruction ww : ww: lance_process (foo, param..);
tel que la fonction lance_process défini dans notre bibliothèque lance la fonction foo par un thread.
3.6.l'opération de lecture /écriture (envoie et réception)
Les opérations de lecture et écriture dans un programme CRP ,sont transformées par le convertisseur comme suit:
• la lecture/écriture dans un canal
Un programme CRP écrit une opération de lecture dans un canal : process foo (inport float a,outport b)
{ int k;
for (i=0;; i++) {w: k=k+a[i];
z: b[i]=k;}}
Le convertisseur transforme l'instruction w à un autre code qui est:
w1: a>consumer (i);
w2: k=k+a>DATA[i];
tel que:
• Dans l'instruction w1, la fonction a>consumer () permet de vérifier si la case est vide ou non.
• Dans l'instruction w2 ,a>DATA[i] permet d'accéder a la case i du canal a.
Pour l'instruction z ,transformer par: b>producer(i,k);
4.contraintes :
Un programme CRP doit utiliser les mots clefs channel,process,inport,et outport dans le sens suivant:
• channel,inport et outport doivent être utilisés seulement pour la déclaration des scalaires. on ne peut pas déclarer un tableau des canaux ou un pointeur vers un port par exemple.
• process peut être utilisé seulement pour des fonctions de qualifications, on ne peut pas par exemple avoir un tableau des processus.[1].
Dans le cas où un programme CRP contient une instruction qui ne respecte pas
cette sémantique , alors la transformation correspondante engendre un code incohérent(des erreurs).
Un autre point qui est très important dans notre conception , qui est la réinitialisation des cases d'un canal donné.
Le convertisseur ajoute une instruction a la fin de chaque processus, a condition que celui ci comporte une instruction de lecture dans un canal . La syntaxe de cette instruction est écrite comme suit:
(*nom de canal*).Rinit(compteur de boucle infini);
telle que: la fonction Rinit permet de réinitialiser la case définie par la valeur de compteur de boucle infinie.
5.Exemple de transformation:
Le code C++ du programme CRP (présente dans la partie de conception (1.3.5)) écrit par notre compilateur comme suit:
• le code correspondant au processus producer void* producer ( void* CANAL ) {
int i ;
t_producer* p_producer;
p_producer=( t_producer* ) CANAL ;
for ( i= 0 ; i< TAILLE_CANAL; i = div( i +1, TAILLE_CANAL ).rem ) { p_producer>x>dproducer ( div( i,TAILLE_CANAL).rem, pthread_self() ); /*1*/
p_producer>x>DATA[div ( i,TAILLE_CANAL).rem]= 2*i; /*2*/
p_producer>x>fproducer ( div( i,TAILLE_CANAL).rem ) ; /*3*/
sleep (2);
} }
• le code correspond au processus consumer void traitement (int a) ;
void* consumer ( void* CANAL ) { int i ;
int j ;
t_consumer* p_consumer;
p_consumer=( t_consumer* ) CANAL ;
for ( i= 0 ; i< TAILLE_CANAL; i = div( i +1, TAILLE_CANAL ).rem ) {
p_consumer>y>consumer ( div( i,TAILLE_CANAL).rem, pthread_self() ); /*4*/
j=p_consumer>y>DATA [div( i,TAILLE_CANAL).rem] ; /*5*/
traitement(j) ;
p_consumer>y>Rinit (i);
sleep (2);
}}
• le code correspond au processus principale (main) extern void* producer ( void* ) ;
extern void* consumer ( void* ) ; int main () {
chanal <int > a ; a.Init (nbr_a) ; void * v_nour ;
printf ("\n lancement de tous les processus") ; t_consumer p_consumer_T1;
p_consumer_T1.y = &a;
pthread_t *th_consumer_T1;
v_nour = (void*) &p_consumer_T1;
th_consumer_T1 = Lance_Process ( consumer , v_nour ) ;
t_consumer p_consumer_T2;
p_consumer_T2.y = &a;
pthread_t *th_consumer_T2;
v_nour = (void*) &p_consumer_T2;
th_consumer_T2 = Lance_Process ( consumer , v_nour ) ; t_producer p_producer_S;
p_producer_S.x = &a;
pthread_t *th_producer_S;
v_nour = (void*) &p_producer_S;
th_producer_S = Lance_Process ( producer , v_nour ) ; Att_process ( th_producer_S) ;
Att_process ( th_consumer_T2) ; Att_process ( th_consumer_T1) ; return 0 ;
}
• le contenue de ficier defin_struct.h
#ifndef DEFIN_STRUCT_H
#define DEFIN_STRUCT_H
#include <stdio.h>
#include <string.h>
#include "Declaration.h"
#include "chanal.c"
#define nbr_a 2
typedef struct s_producer{
chanal <int >* x ; }t_producer ;
typedef struct s_consumer{
chanal <int >* y ;
}t_consumer ;
#endif
• Description du code
Dans ce code les instructions les plus intéressant sont:
La déclaration et l'initialisation de canal:
L'instruction canal <int>a dans la fonction principale ,permet de définir une classe a (template) de type int , qui est le canal a, et l'instruction a.Init (nbr_a) ;
permet d'initialiser les différents paramètres de canal a tel que:
nbr_a présente le nombre des threads qui seront lisent dans le canal a.
La valeur de nbr_a est défini dans un fichier « defin_struct.h ».
Le fichier defin_struct.h rassemble les variables globales et les différents structures utilisées dans l'application.
l'écriture dan un canal:Dans la fonction producer ,l'instruction d'écriture est présenté par les 3 instructions : les instructions 1et 3 (/*1*/ et /*3*/) , permettant de protéger l'accée à la case i de canal par d'autre threads de lecture, et l'instruction /*2*/ écrit une donnée dans cette case.
la lecture de donnée dans un canal:Dans la fonction consumer, l'instruction
de lecture dans une case de canal est présenté par les 2 instructions 4 et 5 (/*4*//*5*/), l'instruction 4 permet de vérifier si la case est vide ou plein ,(s'il est vide la primitive consumer est bloqué par un variable de type sémaphore), et l'instruction 5 lise la donnée (si la case est plein).
Lancement de processus : Comme il présente dans l'instruction principale, l'appel à l'instruction Lance_process (producer,v_nour), permet de lancer la fonction producer par un thread et passer la structure v_nour comme paramètre a ce thread.
la structure (t_consumer et t_producer): Les processus ce sont des fonctions lancées par des threads, alors comme il présente dans (fonction producer et consumer),on donne le type void au résultat retourner par ces fonctions ,avec le paramètre CANAL de type void aussi, la structure t_producer dans (producer) comporte comme des champs ,les
paramètres qui sont défini dans le processus (producer.crp),tel que t_producer définit comme suit:
typedef struct s_producer{
chanal <int >* x ; }t_producer ;
La rinitialisation d'une case de canal: Dans la fonction de producer, l'instruction p_consumer>y>Rinit (i); permet de réinitialiser la case i après la l'écriture de donnée sur cette case.
l'instruction Att_process ( th_consumer_T1) : Cette instruction permet d'attendre le thread qui exécute la fonction consumer jusque la fin de son exécution.
XI.APPLICATIONS
Pour vérifier le bon fonctionnement de notre outil logiciel ,on sera amené à enrichir la base d'exemple du projet SYNTOL,par exemple en important et en adaptant des benchmarks postés sur le web par des projets analogues . dans notre travail nous choisissons une application de codage des flux vidéo avec la norme MPEG.
le code de cette application est disponible en StreamIt (réalisé par le projet de StreamIt)[10][11].
D'abord on présente le principe de cette norme pour le codage des flux vidéos,
puis on présente le schéma correspondant (le graphe de communication des processus ) dans cette application ,ensuite on écrit le code correspondant en CRP,pour en fin compiler ce code par notre compilateur.
1.La norme MPEG
La norme MPEG a été le premier des travaux sur la vidéo et a permis la compression efficace de ce type des données. Cette norme décrit une syntaxe hiérarchique en décomposant une séquence vidéo en plusieurs parties qui ont toutes un rôle dans le codage et la compression de cette séquence.
Le codage et le stockage du MPEG repose sur des outils que nous allons décrire maintenant.
2.structure d'une séquence vidéo MPEG
La figure7 présente la décomposition d'une séquence vidéo avec la norme MPEG
figure7 : la structure d'un séquence vidéo avec la norme MPEG
Une séquence vidéo est décomposée en groupe d'images, et chaque groupe comporte
un certain nombre d'images, et chaque image a son tour est décomposée en d'autres types de format.
Comme c'est illustré dans la figure7 ,le Bloc est le dernier composant utile de cette structure hiérarchique.
Les blocs sont des carrés de 8*8 pixel, afin de simplifier les choses on présente chaque bloc par une matrice de dimension (8,8).
On présente par un schéma les différents processus et la communication entre eux, afin de coder et stocker un bloc.
figure8: schéma de codage/stockage d'un bloc d'image avec MPEG
3. Description de schéma de codage/stockage d'un bloc d'image avec MPEG Le schéma présente par la figure8 constitué un graphe tel que :
– les rectangles sont des processus.
– les flèches sont des canaux de communication entre les processus.
a) Description des processus :
Le schéma présente 5 processus (splitter,zegzag,IDCT,repeat et add).
– VLD: Ce processus produit en sortie une matrice de dimension (8,8) qui présente le bloc, et un vecteur contient des informations concernant le format de l'image.
– zegzag: L'entrée de zegzag est la matrice produite par VLD . ce processus traite les éléments de cette matrice, et produit en sortie un tableau de dimension 8*8 (64 éléments).
– IDCT : L'entrée de ce processus est le tableau produit par le processus zegzag, fait un traitement (transformation inverse de descret cosinus), et produit un tableau de même dimension dans la sortie.
– repeat: L'entrée de ce processus est le vecteur produit par VLD. La sortie est une valeur de type entier .
– add : L'entrée de add est la valeur produite par le processus repeat, et le tableau de dimension (64) produit par le processus IDCT.
Un traitement est fait sur ces éléments, et appele à une fonction.
VLD
repeat
zegzag IDCT
add
note: Ce schéma est une simplification de codage et stockage (certains processus concernant le codage sont supprimés), et non pas un schéma complet de l'application.
L''écriture de l'application de codage en CRP permet d'ajouter à celle ci la propriété de parallélisme , afin d'accélérer l'opération de codage.
Dans le schéma de figure 8 , on remarque que les processus IDCT et zegzag peuvent faire des calculs parallèlement avec le processus repeat.
Le code source de cette application en CRP et en C++ est présenté dans la partie Annexe.
VII.Efficacité et Discussion
Le langage CRP présenté en détail dans ce rapport , et la sémantique qui lui est associée, concernant la structure de canal,les processus ou le mode de lecture et écriture des processus dans les canaux , ont pour but de rendre les applications de traitement de signal , en particulier de flux vidéo, plus fiables et efficaces ,et ceci de différentes manières :
1.la degré de parallélisme :
Le langage CRP permet d' augmenter la degré de parallélisme de ce type d'applications soit au niveau de compilation ou au niveau d'exécution:
• au niveau de compilation : Considérer chaque processus comme un module indépendant , par conséquent ,éviter de recompiler toute l'application en cas d'erreur localle (gain de temps de compilation)[1][5].
• au niveau d'exécution: En CRP tous les processus démarrent ensemble au début de l'application,et sont exécutés en parallèle , avec une garantie de
synchronisation d'accéder dans les canaux (la partie essentielle de notre travail).
Cette propriété conduit aussi a un gain de temps ,par conséquent supporter plus en plus des applications pour systèmes embarqués et en temps réel.
Le mode de lecture et écriture supporté en CRP (une écriture et plusieurs lectures),permet aussi d'augmenter la degré de parallélisme,( contrairement au KPN par exemple qui supporte seulement une seule écriture et une seule lecture) , parce que la propriété de plusieurs lectures permet a plusieurs processus de lire la même donnée (qui écrit une fois).
Ce mode de lecture/écriture permet aussi d'écrire de nouvelles applications de flux vidéo qui ne peuvent être supportes par d'autres langages qui exigent d'autres sémantiques . l'une de ces applications ,par exemple , celle qui consiste à faire plusieurs copies de même image ,dans ce cas on peut lancer le même processus de copiage plusieurs fois, avec des paramètres différents .
Une autre application qui consiste à faire une copie d'une image et de changer l'échelle comme c'est présenté dans la figure suivante :
figure9:schéma de copiage et changement de l'échelle d'une image
2.La syntaxe de langage : Le langage CRP a la même syntaxe du langage C (comme présenté précédemment), cela permet d'utiliser différentes primitives fournies par le langage C et surtout des primitives de manipulation sur les signaux (tel que sigalarm..) . par conséquent il permet d'écrire d'autres applications de traitement de signal.
3.Consommation de mémoire : En CRP on utilise des canaux de taille bornée, et à l'aide de réinitialisation des cases de canal , on peut diminuer la consommation de mémoire, mais cette solution pose un autre problème qui sera présenté par la suite.
Le langage CRP rend un certain nombre d'applications de traitement de signal plus fiables,mais il reste d'autres problèmes qui n'ont pas été encore résolus ,ou des nouveaux problèmes sont rencontrés avec d'autres applications , nous citrons,dans ce qui va suivre, quelques problèmes ,dont certains sont en cours d'études:
• La taille de canal et la réinitialisation des cases:
Le problème majeur présenté dans CRP est le choix de la taille de canal.
dans notre simulation nous avons fixé la taille de canal par une constante , la valeur de cette constante est choisie aléatoirement sans aucune méthode ou algorithme ou tout autre critère .
Le problème principal dans le choix de cette valeur est que , d'un coté,
l'environnement de travail (système embarqué) exige une utilisation très limite de la mémoire, et d'un autre coté , certains type d'applications supportent l'utilisation de la mémoire pour augmenter la degré de parallélisme .
Pour résoudre le problème de taille de canal, on doit chercher à trouver une solution,qui permet de réutiliser le même espace mémoire de canal,parce que il faut limiter la taille de canal (a cause de limite matérielle).
paul en [1], présent une méthode ,qui consiste à réinitialiser une case de canal après chaque utilisation afin de réutiliser cet espace . Cette réinitialisation dépend d'un critère de durée de vie de donnée .
source | | | | | |
copier
changer l'échelle
durée de vie de donnée: C' est le temps entre l'écriture de donnée dans la case et sa dernière utilisation par des processus[1][5].
par conséquent la réinitialisation des cases de canal permet de réutiliser ces cases plusieurs fois.
Cette solution pose un autre problème , c'est comment calculer la durée de vie de donnée?
En StreamIt par exemple ,la méthode INIT, dans la classe filtre , permet de positionner le nombre des données produites et consommées dans le canal au démarrage .
Dans notre simulation l'opération de réinitialisation des cases d'un canal est basée sur le nombre des processus qui lisent dans ce canal, ce nombre est calculé au début de lancement des processus.
Un autre problème peut être posé dans le même sujet , c'est quand la durée de vie d'une donnée dans une case est longue (le temps de garder une donnée dans une case est prolongé).
Ce cas peut être présenté avec des applications qui supportent beaucoup de parallélisme, en particulier, quand on a plusieurs processus de lecture dans un canal,alors il faut garder le contenu de chaque case, jusqu'à la dernière lecture,qui conduit à un cas où le processus qui écrit dans le canal se bloque parce que il n'y a aucune case vide pour écrire des données, et l'opération de réinitialisation attend le délai pour vider une case (voir la figure10).
lecture écriture lecture lecture lecture après n cycle
d'exécution
lecture écriture bloquer
lecture (attente l'opération de lecture)
figure10: le problème de ré initialisation de cases
Une solution peut être présenté pour ce problème qui consiste à limiter le nombre de
processus de lecture dans un canal donné, mais cette solution permet de diminuer la degré de parallélisme.
• problème techniques: L'un des problèmes techniques que nous n'avons pas résolus est le choix de l'ordre de l'instruction de réinitialisation dans le code d'un processus . dans notre simulation on lance l'opération de réinitialisation à la fin de processus.
• D'autres limites qui peuvent être présentés aussi en CRP :
– certaines applications ne supportent pas la sémantique de CRP[1] .
– le projet de SYNTOL est en cours de développement.
– plusieurs recherches et études concernent le calcul d'ordonnancement en CRP[5].
| | | | | | | | | | | | | | | | | | | | | |
IIX.CONCLUSION
Ce stage montre que plusieurs applications de traitement de signal , en particulier de flux vidéo sont possibles à écrire avec le langage CRP qui est le coeur de nos études, parce que ce langage fournit une sémantique et un modèle de communication , qui ne peuvent pas être trouvés avec d'autre langages qui utilisent par exemple le modèle de communication KPN.
L'écriture des applications en CRP permet de vérifier de plus en plus les contraintes de l'environnement de travail (systèmes embarqués).
La sémantique de CRP permet d'augmenter la degré de parallélisme,par conséquent un gain de temps (un temps réel) exigé pour les systèmes embarqués .il permet aussi de diminuer la consommation de mémoire .pour satisfaire cette condition une sémantique et des approches destinées à cet égard et qui consistent par exemple à réutiliser le même espace mémoire plusieurs fois. plusieurs recherche et travaux,concernant ce point, sont en cours .le but c'est d'améliorer ou de trouver d'autres méthodes plus efficaces .
D'une autre part, le travail que nous avons réalisé durant ce stage :
• le compilateur qui permet de compiler une application écrite en CRP, et produire un code correspondant dans un langage de plus haut niveau (langage C++).
• la bibliothèque qui elle fournit des primitives garantissant une synchronisation entre les processus de l' application pour accéder à un espace mémoire partagé entre eux (un canal).
Ce travail permet de supporter de plus en plus d'autres applications de traitement de signal.
par conséquent enrichir la base d'exemples de projet de syntol qui est en cours de développement par l'équipe compsys .
En ce qui concerne l'expérience réalisée pendant le stage, il faut noter l'importance du travail dans un environnement de systèmes embarqués ,et avec des applications qui supportent des modèles de communication comme celui présenté en CRP.
D'autres expériences notées concernant des principes et des outils de compilation tel que le langage ocaml et le c++.
Annexe:
le code en CRP et en C++ réalisé par notre compilateur de l'application de MPEG qui est présenté dans ce rapport, est ecrit comme suit:
1.le code en CRP:
• le fichier vld.crp struct MBloc { float Block [8][8];};
struct FBlock { int format [3];};
process vld (outport struct MBloc bloc[],outport struct FBlock vect[]) {
struct MBloc TMBlock;
struct FBlock TFblock;
int i,j,k;
float s;
for (i=0;;i++) {
for (j=0;j<8;j++) for (k=0;k<8;k++)
TMBlock.Block[j][k]=2*j+k;
bloc[i]=TMBlock;
for (j=0;j<3;j++)
TFblock.format[j]=2*j;
vect[i]=TFblock;
} }
• le fichier zegzag.crp struct TBlock
{
float tableau [64];
};
struct MBloc
{ float Block [8][8];};
process zegzag (inport struct MBloc block[],outport struct TBlock tab[]) {
struct TBlock temp;
int i,j,k;
for (i=0;;i++) {
for (j=0;j<8;j++) for (k=0;k<8;k++)
temp.tableau[8*j+k]= block[i].Block[j][k];
tab[i]=temp;
} }
• le fichier idct.crp float coef[8][8];
struct TBlock {
float tableau [64];
};
process idct (inport struct TBlock entre[],outport struct TBlock sortie[]) {
struct TBlock temp;
int i,j,k;
for (j=0;j<8;j++) for (k=0;k<8;k++) coef[j][k]= j*k;
for (i=0;;i++) {
for (j=0;j<8;j++) for (k=0;k<8;k++)
temp.tableau[8*j+k]=coef[j][k]*entre[i].tableau[8*j+k];
sortie[i]=temp;
} }
• le fichier repeat.crp struct FBlock
{ int format [3];};
process repeat (inport struct FBlock form[],outport int typ[]) {
int temp;
int i,j;
for (i=0;;i++) {
for (j=0;j<3;j++)
temp=temp+form[i].format[j];
typ[i]=temp;
}}
• le fichier jointer.crp struct TBlock
{
float tableau [64];
};
extern void stocker (int a,float aa[64]);
process jointer (inport int entre1[],inport struct TBlock entre2[] ) {
int i,j;
float temp[64];
for (i=0;;i++) {
for (j=0;j<64;j++)
temp[j]= entre2[i].tableau[j];
stocker (entre1[i],temp);
}}
• le fichier mpeg.crp struct FBlock
{ int format [3];};
struct MBloc
{ float Block [8][8];};
struct TBlock {
float tableau [64];
};
process vld (outport struct MBloc bloc[],outport struct FBlock vect[]);
process zegzag (inport struct MBloc block[],outport struct TBlock tab[]);
process idct (inport struct TBlock entre[],outport struct TBlock sortie[]);
process repeat (inport struct FBlock form[],outport int typ[]);
process jointer(inport int tt[],inport struct TBlock aa[]);
void main () {
channel struct MBloc block[];
channel struct FBlock vecteur[];
channel struct TBlock tabl1[];
channel struct TBlock tabl2[];
channel int c[];
V:vld(block,vecteur);
W: zegzag (block,tabl1);
X:idct(tabl1,tabl2);
Y:repeat (vecteur,c);
Z:jointer (c,tabl2);
}
• le fichier stocker.crp (fonction )
void stocker (int a, float b[64]) {
int s;
int i;
float c [65];
for (i=0;i<64;i++) c[i]=b[i];
c[64]=a;
}