Dunod Architectures Logicielles Et Materielles
Dunod Architectures Logicielles Et Materielles
Dunod Architectures Logicielles Et Materielles
Logicielles
et
Matérielles
Introduction 1
Index 571
Bibliographie 577
Introduction
Parmi les thèmes très intéressants que nous avons délibérement écartés (et
réservés pour le tome 2 !) figurent :
– L’étude fine des fonctionnalités d’un système d’exploitation particulier.
Beaucoup de nos références sont inspirées d’unix1 .
– L’étude de la hiérarchie mémoire (cache et mémoire virtuelle), que nous
passons totalement sous silence.
– L’étude détaillée d’un langage d’assemblage d’un processeur donné. Beau-
coup de nos références sont inspirées du sparc2 ou du Motorola 680003 .
– L’étude des techniques de conception de circuits micro-électroniques. Par
exemple nous ne parlons ni de consommation, ni de circuits asynchrones.
– L’étude des techniques d’optimisation des performances des proces-
seurs. Nous ne développons pas les techniques de pipeline, ni celles de
réordonnancement dynamique du flot d’exécution des instructions.
– Les entrées/sorties très particulières que constituent les accès d’un ordina-
teur à un réseau, ce qui demanderait un développement spécifique.
Thèmes
On peut privilégier une approche centrée sur les langages de programma-
tion, leur traduction et la façon dont ils sont exécutés. Sur la figure 0.1 cela
correspond aux flèches en traits gras.
1
marque déposée, et dans la suite de l’ouvrage nous ne préciserons plus que les noms de
systèmes et de machines sont, évidemment, déposés.
2
marque déposée
3
marque déposée
Introduction 3
1 : L’ORDINATEUR
2 : Algèbre de Boole 4 5 : Représentation
6 : Aspects temporels des traitements et
7 : Electronique données
3 : Représentation
des grandeurs
12 13 : Langages
machine et
9 : Eléments de 8 : Circuits d’assemblage
mémorisation combinatoires
10 11 : Circuits
séquentiels
14 : LE PROCESSEUR
19 : Gestion de fichiers
20 : Interprète
21 à 24 :Système complexe de commandes
Interruptions Processus
Fig. 0.1 – Relations de dépendance des principales idées utilisées dans les 24 chapitres.
La zone grisée correspond plutôt au monde du logiciel, la zone blanche au
matériel.
4 Introduction
Index
Les mots en italique apparaissent souvent en index. Dans l’index, les
numéros de page en gras indiquent les occurrences de définition des mots.
Les autres numéros indiquent des occurrences d’utilisation des mots, parfois
antérieures à leur définition, parfois postérieures.
Remerciements
Les idées, principes, techniques, outils, méthodes, présentés dans ce livre
ne sont pas les résultat de nos découvertes. Nous avons reçu des enseigne-
ments, puis nous avons lu, essayé, enseigné. Sans ceux qui nous ont précédés
ce livre n’existerait pas. Sans celles et ceux qui ont enseigné avec nous le mo-
dule Architectures Logicielles et Matérielles au fil des années il serait sans
doute plus pauvre. En particulier Catherine, Danielle, Joëlle, Jean-Louis et
Jean-Paul reconnaı̂tront certaines de leurs bonnes influences. Les mauvaises
viennent d’ailleurs !
Chapitre 1
1. Notion d’information
Une information est une entité abstraite, liée à la notion de connaissance.
Nous nous intéressons naturellement aux informations d’un point de vue tech-
nique en informatique, non d’un point de vue journalistique. Nous donnons
différentes facettes de l’information et séparons l’étude des informations de
celle des objets.
entre les disques anciens et les disques compacts. Il est parfois nécessaire de
réaliser par un dispositif électronique une conversion entre ces deux types de
représentation.
Un chiffre binaire, 0 ou 1, suffit à représenter un bit. Un vecteur de bits
constitue un mot. Les mots de 8 bits sont des octets.
Une même information peut être représentée dans l’ordinateur de façons
diverses : le caractère frappé au clavier est d’abord connu comme un couple
de coordonnées d’une touche au clavier (la touche en deuxième colonne de la
troisième ligne), puis par une séquence de variations de potentiel sur une ligne
électrique liant le clavier et l’ordinateur (combinaison temporelle), puis par un
vecteur de chiffres binaires dont les composantes sont les unes à côté des autres
en mémoire (combinaison spatiale), puis par une représentation sous forme de
matrice de points allumés/éteints sur l’écran.
Pour les informations structurées complexes (en raison des combinaisons)
le codage constitue un langage. Les programmes sont écrits dans des langages
de programmation, les figures sont décrites dans des langages de description
de figures, etc.
Dans le langage courant on assimile souvent l’information, sa valeur, sa
représentation.
Les informations peuvent être transmises d’un point à un autre. Des liai-
sons par fils électriques ou par ondes électro-magnétiques (radio, infra-rouge,
visible, . . .) nous sont familières. A l’intérieur d’un ordinateur la distance est
parfois de moins d’un micron (10−6 m). Quand une fusée transmet vers la Terre
des images de l’espace, la distance est de plusieurs millions de kilomètres. Les
réseaux permettent les transmissions entre ordinateurs.
Il arrive que le codage de l’information comporte une certaine redondance.
Cela peut permettre, si l’on garde l’information en excès, de détecter des er-
reurs de transmission, ou, si le débit d’information est une priorité, de com-
presser la représentation avant de la transmettre.
Mot de m bits
Horloge Initialisation
Adresses
Compteur Accès mémoire
0 1 0 0 1 ... 0 0
Programme
1 1 1 0 ... 0 Programme
Lecture/Ecriture 0 1 0 1 0 ... 1 1
0 0 1 0 ... 0 Registre 2
Instruction
bus données 3
1 0 1 0 ... 1 m
0 0 1 1 ... 1 Registres
Données
de
calcul bus adresses 1 1 1 1 0 ... 0
1 1 1 0 0 ... 1
n
2n − 1
Unité de calcul
2.2.2 Le processeur
Ecran Disque
Clavier
Fils spécialisés
Mémoire
centrale Coupleur Coupleur Coupleur
Processeur
de clavier d’écran de disque
Bus données
Bus adresses
3.1 Matériel
Le matériel est pourtant plus directement accessible à la vue. Nous allons
l’examiner selon trois critères : son aspect, sa technologie et sa fonction.
pas qu’un ordinateur peut parfois être une armoire, ou une carte imprimée,
voire simplement une puce ou circuit comme sur votre carte bancaire. L’écran
n’est pas nécessaire à l’ordinateur. Ce n’est qu’un moyen de communiquer entre
la machine et l’être humain.
porte une partie de mémoire vive et une partie de mémoire morte. Quand on
coupe l’alimentation électrique, la mémoire morte ne perd pas les informations
qui y sont inscrites. La mémoire morte ne peut pas être facilement modifiée.
La mémoire secondaire contient des informations moins directement accessibles
par le processeur. Il faut passer par une interface. Ainsi les disques souples ou
durs sont des mémoires secondaires. Elles sont généralement permanentes :
l’information y reste en l’absence d’alimentation électrique. La carte perforée
a longtemps constitué un support de stockage en informatique. Son avantage
est de pouvoir être lue directement par l’utilisateur humain.
Une mémorisation a lieu aussi dans le processeur qui garde temporairement
des copies de certaines informations dans ses registres.
La fonction de traitement est assurée par le processeur. Il peut lire ou écrire
le contenu de la mémoire principale. Il peut ensuite, comme on l’a vu, exécuter
les instructions lues.
D’autres circuits ont des fonctions de communication entre le processeur
et la mémoire ou entre le processeur et le monde extérieur. Ces circuits d’in-
terfaçage et de communication sont des coupleurs. Les communications avec
le monde extérieur se font à travers des périphériques comme les claviers, sou-
ris, lecteur/graveur/enregistreurs de disques. D’autres types de coupleurs per-
mettent de connecter l’ordinateur à d’autres ordinateurs via un réseau. Dans
les applications industrielles où une chaı̂ne de production est pilotée par ordi-
nateur il serait incongru de considérer la chaı̂ne comme un périphérique ! Du
point de vue du programmeur c’est pourtant le cas.
5. Plan du livre
Le livre comporte six parties.
La première partie donne des fondements pour toute l’informatique, logi-
cielle et matérielle. Les outils mathématiques ne sont pas présentés ici pour
eux-mêmes mais pour être utilisés dans la suite. Les mots binaire, information,
bit, automate, booléen, représentation, état, langage seront alors familiers.
La deuxième partie donne les techniques propres au matériel. Nous y
décrivons toutes les étapes qui permettent de représenter et traiter les vec-
teurs de 0 et de 1 sur du matériel. Les mots puce, système séquentiel, mémoire,
circuit, transistor ne poseront plus de problème.
La troisième partie donne les techniques propres au logiciel. Après cette
partie, on sait tout sur langage, langage d’assemblage, langage machine, saut,
branchement, registre.
La quatrième partie est centrale. On y explique comment le processeur
exécute les instructions. Ceci est fait de façon détaillée, en s’appuyant sur les
connaissances acquises dans les trois premières parties. Après cette partie on
a compris comment du matériel peut traiter du logiciel.
La cinquième partie donne tous les éléments pour construire un ordinateur
au sens où nous venons de le définir. Cela suppose des ajouts de matériel autour
du processeur et de la mémoire et l’introduction de programmes constituant le
système d’exploitation. Après ce chapitre, on sait, de façon détaillée, comment
marche l’ordinateur et comment on le conçoit. On pourrait donc s’arrêter là.
La sixième partie est nécessaire pour le professionnel de l’informatique.
On montre comment peut être mis en place le système qui permet d’accepter
plusieurs utilisateurs effectuant plusieurs tâches simultanément, ou tout au
5. Plan du livre 21
Outils de base de
l’algorithmique logicielle et
matérielle
Chapitre 2
1. Algèbre de Boole
1.1 Opérations
Soit l’ensemble B = {0, 1}. On définit une relation d’ordre total sur cet
ensemble en posant : 0 ≤ 1. A partir de cette relation d’ordre, on définit les
opérations suivantes sur les éléments de B :
Addition : x + y = max(x, y)
Multiplication : x.y = min(x, y)
Complémentation : x̄ = 0 si x = 1 et x̄ = 1 si x = 0
On utilise les termes de somme, produit et complément pour les résultats
de l’addition, de la multiplication et de la complémentation. Le résultat de
ces opérations est détaillé dans la table suivante :
a b a+b a.b ā
0 0 0 0 1
1 0 1 0 0
0 1 1 0 -
1 1 1 1 -
1.2 Définition
Soit A un ensemble non vide comportant deux éléments particuliers notés
0 et 1. On définit sur l’ensemble A deux opérations binaires notées + et . et
une opération unaire notée ¯.
(A, 0, 1, +, ., ¯) est une algèbre de Boole s’il respecte les axiomes suivants :
1. L’addition et la multiplication sont commutatives et associatives.
∀a ∈ A, ∀b ∈ A : a + b = b + a et a.b = b.a
∀a ∈ A, ∀b ∈ A, ∀c ∈ A : (a + b) + c = a + (b + c) et (a.b).c = a.(b.c)
duale
¯=a
ā ←→ ¯=a
ā
duale
a+1=1 ←→ a.0 = 0
duale
a+a=a ←→ a.a = a
duale
a + a.b = a ←→ a.(a + b) = a
duale
a + ā.b = a + b ←→ a.(ā + b) = a.b
duale
a.b + ā.b = b ←→ (a + b).(ā + b) = b
duale
a.b + ā.c + b.c = a.b + ā.c ←→ (a + b).(ā + c).(b + c) = (a + b).(ā + c)
28 Algèbre de Boole et fonctions booléennes
x1 x2 x3 y x1 x2 x3 y
0 0 0 1 1 0 0 1
0 0 1 1 1 0 1 1
0 1 0 0 1 1 0 0
0 1 1 0 1 1 1 1
Fig. 2.1 – Table de vérité de la fonction : y = f (x1 , x2 , x3 )
Règles de De Morgan
duale
a.b = ā + b̄ ←→ a + b = ā.b̄
On peut généraliser à n variables :
duale
x1 .x2 . . . . .xn = x̄1 + x̄2 + . . . + x̄n ←→ x1 + x2 + . . . + xn = x̄1 .x̄2 . . . . .x̄n
2. Fonctions booléennes
2.1 Fonctions booléennes simples
2.1.1 Définitions
On appelle fonction booléenne simple une application de {0, 1}n dans
{0, 1} :
f
(x1 , x2 , ..., xn ) −→ f (x1 , x2 , ..., xn )
(x1 , x2 , ..., xn ) est appelée variable booléenne générale. f est appelée fonction
à n variables. Une valeur donnée de (x1 , x2 , ..., xn ) est appelée point de la
fonction.
La façon la plus simple de définir une fonction est de donner la liste de
ses valeurs en chaque point. On peut le faire sous la forme d’un tableau que
l’on appelle aussi table de vérité. La figure 2.1 donne la table de vérité d’une
fonction à 3 variables.
L’ensemble des points de la fonction forme le domaine de la fonction. On
dit qu’une fonction couvre tous les points pour lesquelles elle vaut 1 (sous-
ensemble du domaine pour lequel la fonction vaut 1). La fonction f définie par
la table 2.1 couvre les points (0, 0, 0), (0, 0, 1), (1, 0, 0),(1, 0, 1) et (1, 1, 1).
Remarque : Une fonction booléenne peut servir à représenter un ensemble :
la fonction vaut 1 en chacun des points appartenant à l’ensemble. On parle de
fonction caractéristique.
x1 x2 f0 f1 f2 f3 f4 f5 f6 f7
0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 0 1 1 1 1
1 0 0 0 1 1 0 0 1 1
1 1 0 1 0 1 0 1 0 1
Les tables de vérité des 16 fonctions à deux variables sont listées dans la fi-
gure 2.2. f1 et f7 correspondent respectivement à la multiplication et l’addition
algébriques vues auparavant.
La relation d’ordre partiel sur les fonctions booléennes générales est définie
comme suit. La fonction générale F = (f1 , f2 , . . . , fm ) est inférieure à la fonc-
tion G = (g1 , g2 , . . . , gm ) si pour tout i dans 1..m, on a fi ≤ gi .
3.2.1 Définition
(0,0,1) (0,1,1) 00 01 11 10
(1,0,1) (1,1,1) 0 * O O
(0,0,0) (0,1,0) 1 O
b
(1,0,0)
a (1,1,0)
(a) (b)
ab
00 01 11 10
cd
00 0 0 1 1
01 1 1 0 0 a.d¯
11 0 0 0 0
ā.c̄.d
10 0 0 1 1
ab ab
00 01 11 10 00 01 11 10
cd cd
00 0 0 0 1 00 0 0 0 1
01 1 1 0 0 01 1 1 1 1
11 1 1 0 0 11 0 0 1 1
10 1 1 0 0 10 0 0 0 0
e=0 e=1
ab 00 01 11
c 10
0 1 1 1 1
a + c̄ 1 0 0 0 1 b̄ + c̄
xi
0 1
fxi fxi
f f
a a
b b b b
c c c c c c
1 0 1 0 1 0 1 1 1 0 1 1
Fig. 2.9 – L’arbre de Shannon d’une fonction f à 3 variables a, b, c avec l’ordre : a <<
b << c et le résultat de la mise en commun de 3 sous-arbres identiques.
est unique.
f f f
a a a
b b b b b
c c c c c
1 0 1 0 1 0
(a) (b)
4. Manipulation de représentations
de fonctions booléennes
Un circuit est en général spécifié par une fonction booléenne. Nous ver-
rons dans le chapitre 8 comment peut être réalisé le processus de synthèse
d’une fonction booléenne vers une technologie cible. Selon les outils, la forme
de départ de la fonction dans ce processus influe énormément sur la forme
du circuit résultant et en particulier sur ses performances en terme de rapi-
dité de calcul, surface en millimètres carrés, consommation électrique, etc. Ces
critères de performance peuvent être traduits de façon plus ou moins précise en
critères simples d’optimisation sur les formes des fonctions booléennes. Nous
justifierons ces critères au chapitre 8.
Nous indiquons dans ce paragraphe quelques formes particulières et
quelques méthodes pour manipuler l’expression d’une fonction booléenne à
des fins d’optimisation en vue d’une implémentation physique.
Les premières méthodes basées sur les tableaux de Karnaugh ont vu le
jour dans les années 50 [Kar53]. Elles permettent d’obtenir une forme po-
lynômiale minimisée de façon manuelle. Des algorithmes permettant de trouver
une forme polynômiale minimale ont ensuite été développé dans les années 60
et 70 [Kun65, Kun67]. Devant l’augmentation de la complexité des fonctions et
des formes nécessaires pour une implémentation dans des technologies de plus
en plus variées, des méthodes basées sur des représentations plus compactes,
en particulier graphe de décision binaire, ont vu le jour depuis.
4. Manipulation de représentations de fonctions booléennes 39
4.2.1 Définitions
Monôme premier : un monôme m est dit premier pour une fonction f si
l’on a : m ≤ f et s’il n’existe pas m = m tel que m ≤ m et m ≤ f .
Base irrédondante : une base est dite irrédondante si, dès que l’on ôte
l’un des monômes qui la composent, on n’a plus une base de la fonction. Une
fonction booléenne peut avoir plusieurs bases irrédondantes.
Le but de la minimisation est donc d’obtenir une base irrédondante
possédant le minimum de monômes.
ab
00 01 11 10
cd
00 1 1 1 0
01 0 1 1 0
11 1 0 0 1
10 1 0 0 0
ab ab ab
00 01 11 10 00 01 11 10 00 01 11 10
cd cd cd
00 Φ Φ 0 0 00 1 1 0 0 00 Φ Φ 0 0
01 0 1 1 0 01 0 1 1 0 01 0 1 1 0
11 0 0 1 Φ 11 0 0 1 1 11 0 0 1 Φ
10 0 0 1 1 10 0 0 1 1 10 0 0 1 1
ab ab
00 01 11 10 00 01 11 10
cd cd
00 0 0 0 1 00 0 0 0 1
01 1 1 0 0 01 1 1 1 1
11 1 1 0 0 11 0 0 1 1
10 1 1 0 0 10 0 0 0 0
f1 f2
M4
ab ab
00 01 11 10 00 01 11 10
cd M6 cd
00 0 0 0 1 00 0 0 0 1
M1 01 1 1 0 0 M7 01 1 1 1 1
11 1 1 0 0 11 0 0 1 1
10 1 1 0 0 10 0 0 0 0
M3
M2 f1 f2 M5
v2 , . . . , vt + vt )). Enfin, on ne garde que les monômes les plus grands.
Exemple E2.6 Sur la figure 2.13 sont représentées les deux bases complètes
des fonctions f1 et f2 . Ces deux bases sont irrédondantes.
Sur la figure 2.14 est représentée la base complète de la fonction générale
F = (f1 , f2 ). Les monômes grisés sont des monômes généraux premiers obtenus
par produits des monômes : (a.b̄.c̄.d, ¯ (1, 0)) et (a.b̄.c̄, (0, 1)) pour (a.b̄.c̄.d,
¯ (1, 1))
et (ā.d, (1, 0)) et (c̄.d, (0, 1)) pour (ā.c̄.d, (1, 1)).
Dans cet exemple, le monôme général M 7 = (ā.c̄.d, (1, 1)) est premier car il
n’existe pas de monôme plus grand que ā.c̄.d à la fois dans f1 et f2 . Le monôme
général M 5 = (a.d, (0, 1)) est premier car dans f2 , il n’existe pas de monôme
plus grand que (a.d) et (a.d) n’est pas un monôme de f1 . La fonction générale
F = (f1 , f2 ) possède 7 monômes premiers généraux M 1 = (ā.d, (1, 0)), M 2 =
(ā.c, (1, 0)), M 3 = (c̄.d, (0, 1)), M 4 = (a.b̄.c̄, (0, 1)), M 5 = (a.d, (0, 1)), M 6 =
¯ (1, 1)), M 7 = (ā.c̄.d, (1, 1)). Les deux bases irrédondantes générales de
(a.b̄.c̄.d,
44 Algèbre de Boole et fonctions booléennes
ab ab
00 01 11 10 00 01 11 10
cd cd
00 0 0 0 1 00 0 0 0 1
01 1 1 0 0 01 1 1 1 1
11 1 1 0 0 11 0 0 1 1
10 1 1 0 0 10 0 0 0 0
f1 f2
Notations :
/1\ : le BDD représentant la valeur 1
/0\ : le BDD représentant la valeur 0
/G, r, D\ : un arbre binaire de racine r, de fils gauche G et de fils droit D
Fonction principale :
LeBdd (e : une expression algébrique) −→ un BDD
{ e étant une expression booléenne, LeBdd(e) est le BDD associé à e. }
LeBdd (1) = /1\
LeBdd (0) = /0\
LeBdd (e1 op e2) = TBop (LeBdd(e1), LeBdd(e2))
LeBdd (op e) = TUop (LeBdd(e))
Fonctions intermédiaires
TBop (b1, b2 : deux BDD) −→ un BDD
{ b1 et b2 sont deux BDD. Il existe une fonction TBop par opérateur bi-
naire traité : elle fabrique le BDD résultat de l’application de l’opérateur
en question aux deux BDD b1 et b2. }
TUop (b : un BDD) −→ un BDD
{ b est un BDD. Il existe une fonction TUop par opérateur unaire pris
en compte : elle produit le BDD résultat de l’application de l’opérateur
au BDD b }
RepCanonique (x une variable ; b1,b2 : deux BDD) −→ un BDD
{ RepCanonique(x, b, b) = b. x étant une variable, b1 et b2 deux BDDs
différents, RepCanonique(x, b1, b2) est le BDD de racine x, de fils
gauche b1 et de fils droit b2. Ce BDD n’est construit que s’il n’existe
pas dans l’ensemble des BDD déjà construits }
Exemple pour l’opérateur OR
{ On applique les règles de simplification triviales associées à l’opérateur
or : vrai or e = vrai, faux or e = e, e or e = e }
TBor (1,b) = /1\
TBor (0,b) =b
TBor (b, 1) = /1\
TBor (b, 0) = b
TBor (b, b) = b
TBor (b1, b2) =
selon b1, b2 { b1 = b2 }
/Ax, x, Ax̄\ = b1 et /Bx, x, Bx̄\ = b2 :
RepCanonique (x, TBor(Ax, Bx), TBor(Ax̄, Bx̄))
/Ax, x, Ax̄\ = b1 et /By, y, Bȳ\ = b2 :
si x << y alors RepCanonique (x, TBor(Ax, b2), TBor(Ax̄, b2))
sinon RepCanonique (y, TBor(b1, By), TBor(b1, Bȳ))
Fig. 2.16 – Construction d’un BDD à partir d’une expression algébrique booléenne
46 Algèbre de Boole et fonctions booléennes
5. Exercices
E2.7 : Proposition logique
Un étudiant dit : je vais faire du ski s’il fait beau ou s’il ne fait pas beau et
que je n’ai pas d’examen à réviser. Cet étudiant est-il sérieux ? sportif ? Pour
répondre donner une forme plus simple de cette proposition logique.
E2.9 : De Morgan
Démontrer les formules de De Morgan à partir des tables de vérité des fonctions
somme, produit et complément.
E2.12 : Ou exclusif
Démontrer que l’opérateur ou-exclusif (noté ⊕) défini par x1 ⊕ x2 = x¯1 .x2 +
x1 .x¯2 est associatif.
f g b
e c
d
semble des N fils peut se trouver dans une des 2N configurations possibles. Les
N fils peuvent représenter 2N informations différentes. On parle aussi des 2N
valeurs possibles d’une information. Il y a là une différence entre le vocabulaire
courant et un vocabulaire technique.
Pour évaluer le nombre de valeurs différentes représentables sur N bits, il
est commode d’avoir en tête les valeurs des petites puissances de 2 et les ordres
de grandeurs des grandes : 20 = 1 ; 21 = 2. Les puissances suivantes sont
4, 8, 16, 32, 64, 128, 28 = 256 et 210 = 1024. Comme 1000 est proche de
1024, il est facile de compléter la suite : 210 ≈ 103 , 220 ≈ 106 , 230 ≈ 109 ,
240 ≈ 1012 .
Les préfixes d’unités correspondants sont kilo, méga, giga, téra. Un kilo-
bit correspond donc à 1024 bits et non à 1000 bits.
Repérer un élément parmi un ensemble de 256 éléments suppose de le lo-
caliser par un numéro codé sur 8 bits. Dans certains contextes ce numéro est
appelé une adresse. Repérer un élément parmi un ensemble de 4 giga-éléments
suppose de le localiser par un numéro codé sur 32 bits.
La notation de logarithme à base 2 est parfois utilisée : si 2N = M, log2 M =
N ; ainsi pour représenter P valeurs différentes il faut au moins R bits, où
R est l’entier immédiatement supérieur au logarithme à base 2 de P . Ainsi
log2 2048 = 11 et pour représenter 2050 valeurs différentes il faut 12 bits.
La correspondance entre la représentation par un vecteur de booléens et la
valeur se fait par une convention, un code. L’ensemble des valeurs codables est
caractéristique du domaine (nombres, couleurs...) Par exemple, si une gamme
de température va de - 10 à + 40 degrés, et si la température est codée sur
9 bits, la précision peut être de l’ordre du dizième de degré (29 = 512 codes
possibles pour 50 degrés). Si la température est codée sur 12 bits la précision
est de l’ordre du centième (212 = 4096 codes possibles pour 50 degrés).
b3 b2 b1 b0 b3 b2 b1 b0 b3 b2 b1 b0
0000 noir 0101 vert 1010 rose
0001 blanc 0110 bleu 1011 gris foncé
0010 rouge 0111 jaune 1100 gris moyen
0011 cyan 1000 orange 1101 vert pâle
0100 violet 1001 brun 1110 bleu pâle
1111 gris pâle
Fig. 3.1 – Codage des couleurs du Commodore 64
b3 b2 b1 b0 b3 b2 b1 b0 b3 b2 b1 b0
0000 noir 0101 violet 1010 vert pâle
0001 bleu 0110 brun 1011 cobalt
0010 vert 0111 gris 1100 rose
0011 cyan 1000 noir pâle 1101 mauve
0100 rouge 1001 bleu pâle 1110 jaune
1111 blanc
Fig. 3.2 – Codage des couleurs pour PC, carte CGA
2. Les naturels
2.1 Représentation des entiers naturels
2.1.1 Numération de position
Les entiers naturels peuvent être écrits de différentes façons (voir par
exemple [Ifr94]). Le système des Romains est encore présent dans certaines
notations, les dates des livres par exemple.
La meilleure représentation est la numération de position dans une base
choisie. En base 10, ou système décimal, on choisit 10 symboles différents, les
10 chiffres décimaux 0, 1, . . . , 9. Ils représentent les valeurs des 10 premiers
naturels. Les naturels suivants s’écrivent avec plusieurs chiffres : un chiffre des
unités, un chiffre des dizaines, des centaines, des milliers, etc.
Si un naturel X s’écrit en base β sur N chiffres xN −1 xN −2 ... x1 x0 , la
correspondance entre la valeur du naturel X et celles des chiffres est donnée
52 Représentation des grandeurs
par l’équation :
N −1
N −1
X= β × valeur(xi )
i
ou, pour simplifier : X = β i xi
i=0 i=0
La correspondance est telle que l’écriture d’un naturel dans une base donnée
est unique. Dans la suite nous ne précisons plus que c’est toujours la va-
leur du chiffre (et non le chiffre lui-même) qui intervient dans les expressions
arithmétiques. En base β, sur N chiffres, tous les naturels compris au sens large
entre 0 et β N − 1 sont représentables. Les nombres plus grands peuvent être
représentés par leur reste modulo β N . C’est ce qui se produit sur les compteurs
kilométriques des voitures : si le compteur a 5 chiffres, quand on a parcouru
100 012 kilomètres, le compteur marque 00 012. Une situation analogue a lieu
pour les angles où on ne donne que le représentant dans l’intervalle [0, 2.π[. En
informatique on rencontre le terme de chiffre de poids faible, pour le chiffre des
unités et, si un naturel est écrit sur 7 chiffres, celui de chiffre de poids le plus
fort pour le chiffre des millions. L’usage dans la vie courante est de ne pas écrire
les 0 en poids forts. A certains endroits, pour des raisons matérielles, c’est une
obligation. Le compteur kilométrique des voitures par exemple. En informa-
tique, on les écrit très souvent. Les machines ont un format pour représenter
les nombres, c’est-à-dire un nombre de chiffres pré-établi. Quand ce nombre de
chiffres est mal choisi, comme par exemple représenter une date avec seulement
deux chiffres décimaux pour l’année, les conséquences sont ennuyeuses. Dans
les documents écrits où il y a risque d’ambiguı̈té, on écrit la base en indice. La
base elle-même est alors écrite en base décimale (ou base 10). Par exemple, le
nombre qui s’écrit 147 en base 10 s’écrit 1042 en base 5 :
14710 = 100100112 = 1739 = 9316 = 10425 = 121103
Il existe une autre représentation conventionnelle : le décimal codé en bi-
naire (DCB) dans laquelle chaque chiffre décimal est écrit en binaire sur 4 bits.
On a alors 14710 = 0001 0100 0111dcb .
3 4 7 B 8
0011 0100 0111 1011 1000
En effet le chiffre hexadécimal B représente le naturel 1110 , qui en binaire
s’écrit 1011, et 347B816 = 001101000111101110002 .
On remarque là une analogie avec le passage de l’écriture décimale à
l’écriture en langue naturelle. Ici figurent l’écriture d’un nombre en base
décimale et son écriture en français (les espaces ne sont pas significatifs) :
Si un naturel est codé sur N bits et qu’il faille le coder sur M bits, avec
M > N , il suffit d’ajouter des 0 en poids forts. A l’inverse, si la représentation
de C a k bits à 0 en poids forts, C peut être représenté sur k bits de moins.
Par exemple 000011002 = 11002 .
2.2.2 Addition
repsi = 1 si ai + bi + repei ≥ 10 et
repsi = 0 si ai + bi + repei < 10
1
Nous emploierons le terme de report pour l’addition et, plus loin, celui d’emprunt pour
la soustraction. La langue courante utilise le terme de retenue dans les deux cas.
2. Les naturels 55
ai bi repei repsi = si =
maj(ai , bi , repei ) ⊕(ai , bi , repei )
0 0 0 0 0
0 0 1 0 1
0 1 0 0 1
0 1 1 1 0
1 0 0 0 1
1 0 1 1 0
1 1 0 1 0
1 1 1 1 1
Fig. 3.4 – Table d’addition
aN−1 aj+1 aj a1 a0
bN−1 bj+1 bj b1 b0
rep
repe(j+1) = repsj e0
sN−1 sj+1 sj s1 s0
rep
s(N−1)
Si deux naturels A et B sont codés sur N bits, leur produit est codé sur
2 × N bits. Si N vaut 1, le produit de A et B est facile à calculer. Sinon,
comme dans l’exercice E3.14 on décompose A et B en parties faible et forte.
2. Les naturels 57
2.2.5 Soustraction
ai bi re rs si
ai bi ee es di 0 1 1 0 0
0 0 0 0 0 0 1 0 1 1
0 0 1 1 1 0 0 1 1 1
0 1 0 1 1 0 0 0 1 0
0 1 1 1 0 1 1 1 0 1
1 0 0 0 1 1 1 0 0 0
1 0 1 0 0 1 0 1 0 0
1 1 0 0 0 1 0 0 1 1
(a) 1 1 1 1 1 (b) ai bi ee es di
3. Les relatifs
3.1 Représentation des entiers relatifs
Pour représenter des entiers relatifs par un vecteur de N booléens, la
première idée qui vient à l’esprit est de représenter la valeur absolue sur N − 1
bits et de réserver un bit pour le signe. Cette idée simple est correcte. On
parle de représentation en signe et valeur absolue. Une autre représentation
est habituellement utilisée. Elle présente des similitudes avec la représentation
d’un angle quelconque par un nombre réel dans l’intervalle [− π, + π[. Nous al-
lons la décrire d’abord de façon très intuitive. Pour cela nous considérons des
nombres sur 4 chiffres. Le même raisonnement vaut pour toute autre taille.
Quand on achète une voiture neuve, le compteur kilométrique indique 0000.
Il indique ensuite 0001, puis 0002. Les voitures à compteur binaire feraient
apparaı̂tre 0001 puis 0010. Imaginons que le compteur décompte lorsque l’on
roule en marche arrière. Avec une voiture neuve, il afficherait successivement
9999, puis 9998. Un compteur binaire montrerait 1111, puis 1110.
On décide de représenter -1 par 1111, puis -2 par 1110, comme sur le comp-
teur kilométrique. Reste à fixer une convention. En effet si l’on roule quinze
3. Les relatifs 59
N −2
N −1
Y = (−2 × yN −1 ) + 2i × yi
i=0
N −1
Y = (−2N × yN −1 ) + 2i × yi
i=0
La correspondance est telle que l’écriture est unique comme pour le cas des
naturels dans une base donnée.
Remarquons que si l’on considère les deux vecteurs binaires représentant
un relatif et son opposé, et si l’on interprète ces deux vecteurs comme les
représentations en binaire de deux naturels, la somme de ces deux naturels
est 2N . C’est l’origine du nom complément à 2N . Ainsi, sur 4 bits, 0101 code
5. En complément à 2, sur 4 bits -5 est représenté par 1011. En base 2, 1011
représente le naturel 11, et 11 + 5 = 16.
Conversion Une difficulté notable vient d’apparaı̂tre, la même que pour les
couleurs en début de chapitre. La question Que représente 100011 ? ou Com-
ment est représenté l’entier K ? n’a pas de sens. Il faut à chaque fois préciser
dans quelle convention, binaire pur ou complément à 2. Comme pour les cou-
leurs, on peut avoir besoin de convertir d’une convention à l’autre les nombres
qui sont représentables dans les deux (comme le brun et le bleu pâle, pour
les couleurs). Pour les nombres sur N bits ce sont les nombres de l’intervalle
[0, 2N −1 − 1]. Ils ont la même représentation dans les deux codes (comme le
cyan et le noir qui ont le même code dans l’exemple des couleurs).
60 Représentation des grandeurs
3.2.2 Addition
Soient A et B représentés en complément à 2 par aN −1 , aN −2 , . . . , a1 , a0
et bN −1 , bN −2 , . . . , b1 , b0 . On a :
N −2
N −2
N −1 N −1
A = (−2 ) × aN −1 + 2 × ai ,
i
B = (−2 ) × bN −1 + 2i × bi
i=0 i=0
N −2
N −2
α= 2 × ai ,
i
β= 2i × bi , γ = (α + β) modulo 2N −1
i=0 i=0
avec :
On a évidemment :
A = −2N −1 × aN −1 + α
et, de même,
B = −2N −1 × bN −1 + β.
Soit re défini par :
α + β = re × 2N −1 + γ.
re vaut donc 1 ou 0. C’est le report sortant du calcul de α + β. γ est la somme
α + β privée de son bit de poids fort re .
Soit S la somme de A et de B.
Puisque γ ∈ [0, 2N −1 − 1], cette inégalité ne peut être vérifiée avec certi-
tude que si
aN −1 + bN −1 − re − 1 ≥ 1 c’est-à-dire si aN −1 + bN −1 − re − 1 = 1.
aN −1 + bN −1 − re + 1 ≤ 0 c’est-à-dire si aN −1 + bN −1 − re + 1 = 0
aN −1 = bN −1 = 0, re =0
aN −1 = bN −1 = 1, re =1
aN −1 = 1, bN −1 = 0, re quelconque
aN −1 = 0, bN −1 = 1, re quelconque
aN −1 bN −1 re rs Interprétation aN −1 + bN −1 − re
1 1 0 1 Premier cas : S < −2N −1 2
0 0 1 0 Deuxième cas : S > 2N −1 − 1 -1
0 0 0 0 Somme représentable 0
1 1 1 1 Somme représentable 1
1 0 x x Somme représentable x
0 1 x x Somme représentable x
62 Représentation des grandeurs
On sait que
S= −2N −1 × (aN −1 + bN −1 − re ) + γ, avec γ ∈ [0, 2N −1 − 1]
En identifiant bit à bit les deux écritures, on voit que pour i ∈ [0, N − 2],
les si ne sont rien d’autres que les chiffres binaires de γ.
De plus, puisque aN −1 + bN −1 − re vaut 0 ou 1, car S est représentable sur
N bits, alors −(aN −1 + bN −1 − re ) = ⊕(aN −1 , bN −1 , re ).
On a reconnu dans re et rs les reports entrant et sortant du dernier étage
d’addition binaire normale des vecteurs ai et bi . Ce qui signifie que les chiffres
binaires de l’écriture de S s’obtiennent de la même façon que les chiffres binaires
de la somme des deux naturels représentés en binaire pur par les ai et les bi .
C’est là tout l’intérêt du codage en complément à 2.
Remarque : Cette propriété est d’une portée pratique considérable.
Elle signifie que le même mécanisme d’addition peut ajouter deux vecteurs bi-
naires sans avoir à tenir compte de l’interprétation, binaire pur ou complément
à 2, qui est faite des opérandes et du résultat. Les chiffres binaires du résultat,
si celui-ci est représentable, sont identiques quelle que soit l’interprétation.
On retrouvera cette propriété dans le chapitre 12 où l’on verra que la même
instruction du langage machine convient pour l’addition, indépendamment du
code choisi, et dans le chapitre 8 où l’on verra que le même circuit combinatoire
additionneur convient pour l’addition indépendamment du code choisi.
Mais, attention, l’information disant si le résultat est représentable ou non
n’est pas la même. En binaire pur le résultat de l’addition est représentable
si et seulement si rs = 0. En complément à 2 le résultat de l’addition est
représentable si et seulement si rs = re .
L’exercice corrigé E3.6 donne des exemples qui concrétisent ces équations.
3. Les relatifs 63
3.2.3 Soustraction
Puisque l’addition est connue, ainsi que le passage à l’opposé, la soustrac-
tion ne pose pas de problèmes : il suffit de se souvenir que A − B = A + −(B).
Comme pour l’addition, les constructeurs de processeurs donnent l’expres-
sion booléenne du bit V de débordement après une soustraction :
V = aN −1 .bN −1 .s + aN −1 .bN −1 .s
La division par 2 des entiers relatifs, qui revient à diviser par 2 la valeur
absolue de l’entier en conservant son signe, nécessite quelques précautions pour
les entiers négatifs impairs.
Le décalage arithmétique ne tient en effet aucun compte de la valeur du bit
de poids faible. Or changer de 0 à 1 le bit de poids faible d’un entier pair a pour
effet d’en augmenter la valeur absolue s’il est positif ou nul, et au contraire de
la diminuer s’il est négatif.
Pour en tenir compte, il faut au préalable ajouter 1 aux seuls entiers négatifs
avant d’effectuer le décalage vers les poids faibles. Si l’entier est pair, ceci ne
modifie que le bit de poids faible qui est ensuite ignoré lors du décalage.
Si l’entier est impair, cette opération le ramène à l’entier pair de valeur
absolue immédiatement inférieure. Ainsi, pour l’entier -7, on appliquera en
fait le décalage sur l’entier -6.
5. Les caractères
Les caractères alphabétiques, numériques, typographiques (parenthèse, vir-
gule, etc.) et certains caractères non imprimables (fin de ligne, fin de fichier,
etc.) sont habituellement représentés sur 7 bits selon un code normalisé nommé
code ASCII pour American Standard Code for Information Interchange (Cf. Fi-
gure 3.6).
Le code ASCII est tel que : l’entier représentant un chiffre vaut la valeur
du chiffre plus 48 ; les entiers correspondant aux codes de deux lettres sont
ordonnés comme les deux lettres dans l’alphabet si les deux lettres sont toutes
les deux en majuscules ou en minuscules ; la différence entre le code d’une
majuscule et de la minuscule correspondante est 32, c’est-à-dire une puissance
de 2.
Sur une machine unix la commande man ascii fournit en hexadécimal
le tableau des codes correspondant aux caractères. Comme on le voit sur la
figure 3.6, 2316 est le code hexadécimal de # et 2016 celui de l’espace ; del, de
code 7F16 , est le caractère d’effacement. Les codes inférieurs à 1F représentent
des caractères non affichables.
Ce code ne permet pas de représenter les lettres accompagnées de diacri-
tiques (accents, cédille, tréma, tilde, petit rond, etc.) dans les langues qui les
utilisent (c’est-à-dire presque toutes les langues européennes !). Des extensions
à 8 bits, puis à 16 sont proposées (UNICODE), mais les standards sont diffi-
ciles à établir. Le problème de pouvoir coder en binaire l’ensemble de toutes
les formes écrites des principales langues écrites du monde n’est pas encore
totalement résolu. Cela pose de nombreux problèmes lors des transmissions de
fichiers contenant des textes.
66 Représentation des grandeurs
20 21 ! 22 ” 23 # 24 $ 25 % 26 & 27 ’
28 ( 29 ) 2A * 2B + 2C , 2D - 2E . 2F /
30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
38 8 39 9 3A : 3B ; 3C < 3D = 3E > 3F ?
40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
48 H 49 I 4A J 4B K 4C L 4D M 4E N 4F O
50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
58 X 59 Y 5A Z 5B [ 5C \ 5D ] 5E ˆ 5F
60 ‘ 61 a 62 b 63 c 64 d 65 e 66 f 67 g
68 h 69 i 6A j 6B k 6C l 6D m 6E n 6F o
70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
78 x 79 y 7A z 7B { 7C | 7D } 7E ˜ 7F del
7. Exercices
E3.1 : Expression booléenne d’une propriété arithmétique
Considérons un naturel A codé sur N bits. Donner l’expression booléenne
caractérisant les bits de A pour que 10 × A soit aussi codable sur N bits.
a.b.(a ⊕ b) = a.b.(a ⊕ b) = 0
V = aN −1 .bN −1 .s + aN −1 .bN −1 .s
aP bP re rs = sP = V =
maj(aP , bP , re ) ⊕(aP , bP , re ) r e ⊕ rs
signe A signe B indic. C indic. N indic. V
0 0 0 0 0 0
0 0 1 0 1 1
0 1 0 0 1 0
0 1 1 1 0 0
1 0 0 0 1 0
1 0 1 1 0 0
1 1 0 1 0 1
1 1 1 1 1 0
−231 ou U > 231 − 1), on dit que l’addition déborde. Mais dans ce cas U
est représentable sur 64 bits (33 suffiraient). Ecrire un programme en langage
d’assemblage qui donne toujours la somme U dans deux registres %l3, %l2.
On peut évidemment faire l’exercice analogue pour la différence.
m2 m1 m0 e1 e0 pièce m2 m1 m0 e1 e0 pièce
101 0 0 5 centimes 010 1 0 2 Francs
001 0 1 10 centimes 101 1 0 5 Francs
010 0 1 20 centimes 001 1 1 10 Francs
101 0 1 50 centimes 010 1 1 20 Francs
001 1 0 1 Franc
1. Un langage d’actions
Le langage d’actions que nous décrivons brièvement ci-dessous est tiré
de [SFLM93]. Nous supposons connues les notions de variable dans un lan-
gage de programmation impératif, de type des données.
1.4 Affectation
L’action de base dans un langage d’actions est l’affectation, qui permet de
modifier la valeur d’une variable. On la note par une flèche orientée à gauche :
X ←− expr
T[3+z].u ←− expr
La partie gauche d’une affectation doit pouvoir désigner un emplacement
mémoire (nous y revenons dans le paragraphe 3.) ; la partie droite est une
expression, dont le type doit être compatible avec le type de la partie gauche.
Les langages de programmation proposent des notions de compatibilité de
types plus ou moins riches, des vérifications statiques associées, ainsi que des
conversions dynamiques implicites. Nous nous contenterons ici d’exiger que les
types des parties gauche et droite soient identiques.
Toutefois on peut avoir besoin d’écrire x ←− y, où x est un réel et y un en-
tier. Le codage binaire des entiers étant fort différent de celui des réels (Cf. Cha-
pitre 3), la représentation en mémoire de la variable y est nécessairement
différente de celle de x.
Pour mettre en évidence la conversion que cache ainsi l’affectation,
nous utiliserons des fonctions de conversion de type (ou de changement de
représentation mémoire) explicites :
EntierVersRéel : un entier −→ un réel
{ EntierVersRéel (a) est le réel de valeur a }
Naturel31 : le type entier sur [0, 232−1 − 1]
Entier32 : le type entier sur [−232−1 , 232−1 − 1]
Naturel31VersEntier32 : un Naturel31 −→ un Entier32
{ NaturelVersEntier (n) est l’entier de valeur n }
Nous revenons sur la traduction en assembleur de ces fonctions au cha-
pitre 13. Nous verrons en particulier que la traduction en langage d’assemblage
de la fonction Naturel31VersEntier32 est un programme vide ! Au chapitre 3,
paragraphe 3.1, nous signalions déjà ce cas.
80 Représentation des traitements et des données...
Une construction moins courante est le selon, qui permet de décrire une
analyse par cas exhaustive et sans duplication de cas, pour les valeurs d’une
ou plusieurs expressions de type quelconque. Dans l’exemple qui suit, A1, A2
et A3 représentent des actions quelconques.
X : un entier
Y : un caractère
selon X, Y
X ≥ 0 et Y = ’a’ : A1
X ≥ 0 et Y = ’a’ : A2
X < 0 : A3
Cette structure générale doit souvent être codée par une série d’expressions
conditionnelles si ... alors ... sinon enchaı̂nées, comme en Pascal, en C, ... Les
structures case et switch de ces langages ne permettent en effet que des
conditions de la forme expr = constante, pour des types dont les constantes
ont une notation dans le langage, c’est-à-dire les entiers, caractères, types
énumérés. La structure selon à conditions quelconques existe en Lisp (cond),
mais sa sémantique est séquentielle et les différentes conditions ne sont pas
nécessairement exhaustives.
Nous utilisons par ailleurs 3 structures itératives : parcourant (qui corres-
pond au for de Pascal, C, Ada, ...), tantque (qui correspond au while de
Pascal, C et Ada), répéter ... jusqu’à (qui correspond au do ... while de C,
au repeat ... until de Pascal, au loop ... while de Ada).
La sémantique de ces constructions est précisée par leur traduction en ma-
chines séquentielles à actions (ou organigrammes) au chapitre 5.
On peut déjà ramener la structure parcourant à une structure tantque :
i parcourant [a .. b] : A
{ Est équivalent à : }
i : un entier sur [a .. b+1]
i ←− a
tantque i ≤ b :
A ; i ←− i + 1
1. Un langage d’actions 81
1.7 Entrées/Sorties
On utilisera les actions Lire et Ecrire, pour tout type de données, et avec un
nombre quelconque de paramètres.
Les paramètres de Lire sont des résultats, ceux de Ecrire sont des données.
Une utilisation typique est décrite ci-dessous :
lexique : x, y : des entiers
Ecrire (”Donnez deux entiers : ”) ; Lire (x, y)
Ecrire (”Somme des deux entiers : ”, x+y)
taille possible : une unité adressable (voir toutefois le paragraphe 2.4.2 pour
le cas particulier des tableaux de booléens, où l’on peut espérer gagner de la
place). Il faut convenir d’un codage des deux constantes vrai, faux parmi les 2k
configurations d’une unité adressable de k bits.
Rien n’empêche, a priori, de choisir, vrai = 4210 et faux = 7710 (sur un octet
par exemple). Le choix du bon codage dépend essentiellement de la réalisation
des opérations dans lesquelles intervient un opérande ou un résultat de type
booléen. Il faut penser aux opérations internes du type booléen (conjonction,
disjonction, ...) et à la fabrication de valeurs booléennes par comparaison de
deux entiers par exemple (qui apparaı̂t bien sûr dans si X < Y alors ... mais
aussi dans des expressions de la forme : B ←− (X < Y)).
Pour vrai = 4210 et faux = 7710 , il est difficile de décrire la conjonction de
deux booléens a et b plus simplement que par :
si a=42 alors si b = 42 alors 42 sinon 77 sinon 77.
Dans le langage C, le choix est le suivant : 0 représente faux, toute autre
valeur représente vrai ; une conjonction peut alors être réalisée à l’aide de
l’opérateur et logique disponible sur tous les processeurs.
La situation décrite ci-dessous n’est pas la plus générale que l’on pourrait
imaginer. Elle est guidée par les contraintes matérielles de liaison entre le
processeur et la mémoire, que nous étudierons au chapitre 15.
Tout d’abord, nous ne nous intéressons qu’au cas de blocs d’unités adres-
sables en nombre égal à une puissance de 2 (pour ne pas perdre d’espace
d’adressage, Cf. Chapitre 15). D’autre part, sur la plupart des machines, les
accès ne sont permis que lorsque l’adresse est un multiple de la taille du trans-
fert (les autres accès ne sont pas nécessairement implémentés parce qu’ils sont
moins efficaces). Cette restriction est connue sous le nom de contrainte d’ali-
gnement mémoire.
Les contraintes matérielles d’accès à la mémoire ont également pour
conséquence que les accès simultanés à un nombre quelconque d’unités adres-
sables ne peuvent pas constituer des opérations élémentaires dans une machine
(un processeur) usuel. Les affectations de mémoire présentées ci-dessous, in-
dicées par le nombre d’unités à transférer, sont en petit nombre, fixé.
Nous noterons ←− k une affectation de taille k, c’est-à-dire un transfert
simultané de k unités adressables. Nous considérons par la suite les affecta-
tions :
x ←− 1 MEM[a]
{ L’unité adressable d’indice a dans le tableau MEM est copiée dans la variable
x (supposée de taille adéquate) }
x ←− 2 MEM[a]
{ Valide si a est multiple de 2. Les deux unités adressables d’indices a et a+1
sont copiées dans la variable x (supposée de taille adéquate). }
x ←− 4 MEM[a]
{ Valide si a est multiple de 4. Les quatre unités adressables d’indices a, a+1,
a+2 et a+3 sont copiées dans la variable x (supposée de taille adéquate). }
Il existe en général une opération élémentaire de transfert de 4 octets dans
les machines dites 32 bits, une opération de transfert de 8 octets dans les
machines 64 bits, ...
Les n-uplets, de même que les entiers suffisamment grands, demandent plu-
sieurs unités adressables. On utilise lorsque c’est possible des unités contiguës.
Considérons les définitions de type :
T1 : le type entier dans [−232−1 , 232−1 − 1]
T2 : le type entier dans [−216−1 , 216−1 − 1]
Structure12 : le type < x : un T1, y : un T2 >
Structure21 : le type < x : un T2, y : un T1 >
Une valeur de type Structure12 occupe 6 unités adressables consécutives,
d’adresses a, a + 1, ... a + 5. Le champ x commence à l’adresse a, et le champ
y à l’adresse a + 4.
86 Représentation des traitements et des données...
est tel que deux objets de type T’ peuvent toujours être placés côte à côte
en mémoire, il n’y a pas de place perdue. C’est le cas par exemple pour
T’ : le type < c1, c2, c3 : des caractères >.
En revanche, si T’ est le type Structure12 étudié au paragraphe précédent,
on doit ménager un espace de deux octets entre deux éléments, de manière à
satisfaire la contrainte d’alignement sur des adresses multiples de 4 du champ
x.
On peut conserver la formule qui donne l’adresse T[i] en fonction de l’adresse
a de début de T, à condition de redéfinir la notion de taille nécessaire à la
représentation d’un type. Par exemple, taille align (Structure12) = 8, et non 6.
Cas particulier des tableaux de booléens Nous avons vu plus haut qu’un
booléen seul occupe un octet. Lorsqu’on considère un tableau de booléens,
il devient intéressant d’essayer de gagner de la place en choisissant une
représentation plus compacte. Considérons le tableau T défini par :
T : un tableau sur [0, N−1] de booléens
Les éléments de T peuvent être placés en mémoire à partir d’une adresse a, à
raison d’un élément par bit. Le tableau complet occupe alors N/8 octets au
lieu de N . La position de l’élément de rang i est déterminée par : le numéro
de l’octet dans lequel il se trouve ; le numéro de bit dans l’octet. On obtient
ces deux informations en prenant respectivement le quotient et le reste de la
division entière de i par 8.
Lexique
N : l’entier ... ; i : un entier dans [0..N]
T : un tableau sur [0..N−1] d’entiers dans [−232−1 , 232−1 − 1]
algorithme
i ←− 0
tant que i < N
T[i] ←− 2*i + 1
i ←− i+1
La première transformation consiste à faire apparaı̂tre le tableau MEM qui
modélise la mémoire, et l’installation des éléments de T en mémoire. On note
aT l’adresse de début de T en mémoire. On obtient :
lexique : E : l’entier taille align(entier dans [−232−1 , 232−1 − 1])
algorithme :
i ←− 0
tant que i < N
MEM [aT + E * i] ←− 2*i + 1
i ←− i+1
La deuxième transformation consiste à ajouter une variable redondante Ad
pour représenter l’adresse de l’élément courant en mémoire. Cette variable est
liée à l’indice i du tableau par la propriété Ad = aT + E * i que l’on installe
avant la boucle, et que l’on maintient en modifiant Ad lors de toute modification
de i. On obtient :
i ←− 0 ; Ad ←− aT + E * i
tant que i < N
{ Invariant : Ad =aT + E * i }
MEM [Ad] ←− 2*i + 1
i ←− i+1 ; Ad ←− Ad + E
MEM MEM
T a T[0,0] a T[0,0]
T[0,1] T[1,0]
0 1 2 3=N-1 T[0,2] T[2,0]
T[0,3] T[0,1]
0 T[0,0] T[0,1] T[0,2] T[0,3]
T[1,0] T[1,1]
1 T[1,1] T[2,1]
T[1,0] T[1,1] T[1,2] T[1,3] ........ ........
2=M-1
T[2,0] T[2,1] T[2,2] T[1,3]
T : un Tab
La représentation de T en mémoire nécessite N × M × taille align(T’) unités
adressables.
La figure 4.1 illustre les choix de placement des éléments de T dans le
tableau MEM, dans le cas où N = 4 et M = 3. Noter que la représentation de
T sous forme de matrice (a), et le choix de la dimension qu’on appelle ligne sont
conventionnels ; nous convenons ici que dans l’expression T[i,j], i représente un
numéro de ligne et j un numéro de colonne.
Dans le cas (b), on range les éléments de T ligne par ligne, et l’adresse de
l’élément T[i,j] s’exprime par la formule : a + (i × N + j) × taille align (T’), où
a est l’adresse de début du tableau.
Dans le cas (c), on range les éléments colonne par colonne, et l’adresse de
l’élément T[i,j] s’exprime par la formule : a + (j × M + i) × taille align (T’).
Noter la symétrie des deux formules.
Remarque : Nous réservons le terme de tableau à deux dimensions aux
structures implantées de manière contiguë. En Java, on appelle tableau à deux
dimensions une structure de données plus compliquée qui consiste en un ta-
bleau à une dimension de pointeurs sur des tableaux à une dimension. Dans
ce cas les lignes (ou colonnes) ne sont plus nécessairement contiguës.
d’affectations.
fictif LaSéquence
inf sup
considérons ici que le tableau MEM est partitionné en deux : une première
portion P 1, qui va de l’indice 0 à l’indice T , dans laquelle on trouve en par-
ticulier les variables du lexique global ; une deuxième portion P 2 qui va de
l’indice T + 1 à l’indice du dernier élément tmem−1, dans laquelle on trouve
les blocs alloués dynamiquement et les informations de gestion du tas.
Allouer : une action (le résultat : un pointeur ; la donnée : un entier > 0)
{ Allouer (p, n) réserve dans la zone de mémoire comprise entre les indices
T +1 et tmem−1 une zone contiguë de n éléments, démarrant sur une frontière
multiple de n. p est l’adresse de la première unité adressable de cette zone
réservée. Si l’espace disponible est déjà entièrement occupé, la valeur finale
p = NIL exprime l’impossibilité d’allouer.
C’est une action générique, qui convient pour tout type de pointeur. }
Lorsque la pile est de taille suffisante pour l’utilisation qui en est faite, ou
lorsque qu’on ne veut pas s’intéresser au problème du débordement (c’est-à-
dire une tentative d’insertion lorsque la pile est pleine), on utilisera une action
96 Représentation des traitements et des données...
Empiler sans paramètre résultat booléen. Dans ce cas, l’état final d’une pile qui
était pleine lors de l’empilement d’un élément, est non spécifié.
De même, si l’on ne s’intéresse pas au problème d’accès à la pile vide, ou si
l’on sait que l’action Dépiler n’est jamais appelée avec une pile vide, on peut
utiliser une action Dépiler sans paramètre résultat booléen.
Sous les mêmes hypothèses que pour la pile, on s’autorise les actions Entrer
et Sortir sans paramètres résultats booléens.
6. Exercices
E4.1 : Codage des entiers : petit bout ou gros bout
Considérons deux chaı̂nes de caractères dont on veut réaliser la comparai-
son lexicographique (autrement dit déterminer laquelle vient en premier dans
l’ordre alphabétique). Ces chaı̂nes sont représentées en mémoire de manière
contiguë, chaque caractère occupe un octet et il n’y a pas de place perdue.
Pour accélérer la comparaison, on utilise des opérations de comparaison d’en-
tiers codés en binaire pur sur 32 bits, c’est-à-dire qu’on compare les caractères
4 par 4. Le choix de représentation en mémoire des entiers (petit bout ou gros
bout, Cf. Paragraphe 2.2.4) a-t-il une influence sur la correction du résultat ?
opérations ensemblistes
Les vecteurs booléens peuvent représenter des ensembles, ou, plus exactement,
un vecteur booléen de N bits peut représenter une partie d’un ensemble à N
éléments : le bit de rang x est à 1 si et seulement si l’élément x appartient à
l’ensemble. (Cf. Paragraphe 4. du chapitre 3). On considère les types :
Elem : le type entier dans [0..31] ; EnsElem : le type ensemble d’Elems
E1, E2 : des EnsElem
98 Représentation des traitements et des données...
N : un entier > 0
Matrice : un tableau sur [0..N-1, 0..N-1] d’entiers
∀n ≥ 0, (qn , en , qn+1 ) ∈ T
Une transition (q, e, q ) exprime que, si la machine est dans l’état q, et qu’elle
reçoit l’entrée e, alors elle passe dans l’état q . La séquence de sorties Ss =
s0 , s1 , ... est ensuite définie par l’intermédiaire de la séquence d’états :
∀n ∈ N, sn = f (qn )
q 0 = q0 ∀n ≥ 0, (qn , en , sn , qn+1 ) ∈ T
∀q ∈ Q, {e ∈ E | ∃q1 ∈ Q, s ∈ S, (q, e, s, q1 ) ∈ T } = E
c c c c
1 2 1 2
a b a b a,b
a a
3 b 4 3 b 4 a,b,c
c
(a)
a, b, c 5
(b)
AUCUNE rien
Attente
Pièces
s5
s2
fs fs
rien Trop rien
perçu 2F reçus
R,C,B C,B
s2 s1
s1
s5
1F reçu rien
AUCUNE
Fig. 5.3 – Une séquence d’exécution du contrôleur de la machine à café : chaque ligne
correspond à un instant différent ; le temps passe du haut vers le bas dans le
tableau.
2. Machines séquentielles avec actions 109
C C C?
faux vrai
(a) (b)
Fig. 5.5 – Machine de Moore avec actions et organigramme. (a) : un état de machine
de Moore avec actions (C est une condition booléenne et A une action) ; (b) :
une portion d’organigramme qui représente le même traitement.
2.1 Définition
On se donne un lexique (au sens défini chapitre 4) qui définit des types,
des variables typées, des fonctions et des actions sans paramètres. Parmi les
fonctions on distingue les prédicats, qui sont à résultat booléen. Le prédicat
constant vrai et l’action vide vide sont toujours définis, et jouent un rôle par-
ticulier dans les manipulations de machines séquentielles à actions (Cf. Para-
graphe 2.4).
Une machine séquentielle avec actions est une machine à états finie dont le
vocabulaire d’entrée est l’ensemble des prédicats : l’évaluation d’un prédicat
représente une entrée de la machine, au sens du paragraphe 1.1. Les transitions
sont donc étiquetées par des prédicats. L’ensemble des actions constitue le
vocabulaire de sortie.
Une machine de Moore avec actions est très similaire aux organigrammes
classiques, ainsi que le montre la figure 5.5.
Les machines de Mealy avec actions sont étudiées dans [SFLM93]. Elles sont
une extension naturelle des algorithmes obtenus comme codage systématique
des machines de reconnaissance des langages réguliers (paragraphe 1.4). Nous
ne les étudierons pas ici.
2. Machines séquentielles avec actions 111
non C C
B A C A
non C
A vrai
vrai vrai
vrai B
Répéter A jusqu’à C
tantque C faire [ A ; tantque D faire [ B ] ; E ]
non C A
C
C
A
non D vrai
non C C vrai
D vrai
A
B
vrai
X ; si C alors A
E
non C
X
vrai
C
non C X
A C
vrai non C A
vrai
Fig. 5.6 – Traduction des structures de contrôle en machines séquentielles avec actions.
Les états non étiquetés portent implicitement l’action vide.
2. Machines séquentielles avec actions 113
00
10
21
5 31
42
52
3 63
2 73
83
94
0 10 4
0 5 6 11 12 11 5
12 5
Fig. 5.7 – Tracé d’un segment dont les extrémités sont les points de coordonnées (0, 0)
et (12, 5). Le trait blanc est idéal, les pixels noirs sont obtenus par l’algorithme,
les pixels gris pourraient sembler candidats.
le segment de cette droite qui va du point (0, 0) au point (m, n). Les points
n’ayant que des coordonnées entières, il faut noircir un ensemble de points (ou
pixels, pour picture element) aussi proches que possibles de la droite idéale.
Remarque : Sans perte de généralité, nous traitons le cas où 0 ≤ n ≤ m.
Les autres cas s’obtiennent aisément par des transformations simples où le
point de coordonnées (j, k) devient (±j, ±k) ou (±k, ±j).
L’équation de la droite étant y = (n/m)x, avec m et n entiers, pour tout
point de coordonnées entières (j, k), il est possible de calculer un écart par
rapport à la droite idéale : k = (n/m).j − ou = (n/m).j − k. Le critère de
proximité retenu est le suivant : tout point de coordonnées (j, k) doit être tel
que : || ≤ 12 .
Evaluons la proximité relative de deux pixels par rapport à la droite idéale
avec les valeurs m = 12, et n = 5 (Cf. Figure 5.7). Pour le pixel d’abcisse 1,
calculons l’écart à la droite idéale de (1, 1) qui apparaı̂t en grisé, et de (1, 0)
7 5
qui est donné par l’algorithme ; pour (1, 1), = − 12 et pour (1, 0), = 12 .
C’est le point (1, 0) qui est donné par l’algorithme. Pour le point d’abscisse 6,
les deux points (6, 2), en grisé, et (6, 3), en noir, donnent la même valeur de ||.
De || ≤ 12 nous pouvons déduire :
− 12 ≤ ≤ 12
− 12 ≤ (n/m).j − k ≤ 12
−m ≤ 2.n.j − 2.m.k ≤ m
−2m ≤ 2.n.j − 2.m.k − m ≤ 0
Posons Δ = 2.n.j − 2.m.k − m. On remarque alors que lorsque j augmente
de 1, Δ augmente de 2.n ; lorsque k augmente de 1, Δ diminue de 2.m. La
construction de l’algorithme de calcul des coordonnées des pixels successifs
114 Représentation des traitements et des données...
lexique
n : l’entier ... ; m : l’entier ...
T : un tableau sur [0..m, 0..n] de booléens
j, k, Δ : des entiers
algorithme
k ←− 0 ; j ←− 0 ; Δ ←− − m
{ Valeur initiale de l’écart : l’abscisse j vaut 0, l’ordonnée k vaut 0, donc
Δ = −m }
tant que j ≤ m :
{ Invariant : 0 ≤ j ≤ m et -2*m ≤ Δ ≤ 0 }
Tj,k ←− vrai { Le point de coord. j, k doit être affiché }
{ Pour le point suivant, on augmente j de 1 }
j ←− j + 1 ; Δ ←− Δ + 2*n
si Δ > 0
{ Si Δ est devenu trop grand, on le ramène à une valeur conve-
nable en augmentant l’ordonnée courante }
k ←− k + 1 ; Δ ←− Δ − 2*m
{ −2 ∗ m ≤ Δ ≤ 0 }
{ Les variables : }
Fini, Δpos : des booléens
{ Les actions : }
Init : une action : j ←− 0 ; k ←− 0 ; Δ ←− −m
MajTetIncrAbs : une action
Tj,k ←− vrai ; j ←− j + 1 ; Δ ←− Δ + 2 * n
IncrOrdonnée : une action : k ←− k + 1 ; Δ ←− Δ − 2 * m
CalculFini : une action : Fini ←− j > m
CalculΔpos : une action : Δpos ←− Δ > 0
{ Les prédicats : }
EstFini : −→ un booléen : fini
ΔEstPos : −→ un booléen : Δpos
Fig. 5.9 – Lexique de machine séquentielle avec actions représentant l’algorithme de Bre-
senham
Init
vrai
CalculFini
non EstFini
EstFini MajTetIncrAbs
vrai
non ΔestPos
CalculΔpos
vrai ΔestPos
IncrOrdonnee
C3
non C3
C3 A3
C2
C1 C2
non C2
A1 A2 A3
A2
C1
vrai vrai vrai
A1
non C1
vrai
vrai
vrai
lexique
B1 : le booléen ... ; B2 : le booléen ... ; N : l’entier ... ; i : un entier
T : un tableau sur [0..N] de booléens
CondT : un entier −→ un booléen { une propriété portant sur un entier }
algorithme 1 :
i ←− 0
tant que i ≤ N
si CondT(i) alors Ti ←− (Ti et B1) sinon Ti ←− (Ti ou B2)
i ←− i + 1
algorithme 2 :
i ←− 0
tant que i ≤ N
Ti ←− (CondT(i) et (Ti and B1)) ou (non CondT(i) et (Ti ou B2))
i ←− i + 1
{ lexique : }
C1, C2 : des booléens
{ les actions : }
Init : une action (la donnée-résultat i : un entier) : i ←− 0
CalculC1 : une action (les données i : un entier, N : un entier) : C1 ←− i ≤ N
CalculC2 : une action (la donnée i : un entier) : C2 ←− CondT(i)
AndT : une action (les données : x : un booléen, i : un entier) : Ti ←− Ti et x
OrT : une action (les données : x : un booléen, i : un entier) : Ti ←− Ti ou x
ActCond : une action (les données : x1, x2 : deux booléens, i : un entier)
Ti ←− (CondT(i) et (Ti et x1)) ou (non CondT(i) et (Ti ou x2))
{ les prédicats : }
EstC1 : −→ un booléen : C1
EstC2 : −→ un booléen : C2
Init
vrai
CalculC1
non EstC1 EstC1
CalculC2
OrT(B2,i) AndT(B1,i)
Incr(i)
Init
vrai
CalculC1
non EstC1
EstC1
ActCond(B1, B2, i)
vrai
vrai
Incr(i)
G G
(a)
temps temps
β α
G G
(b)
temps temps
GD GD
1 1
(c)
0 0
GD GD
1 1
(d)
0 0
(A) (B)
En revanche, dès que l’on considère plusieurs grandeurs, les paliers (ou,
de manière équivalente, les fronts) sont superposés. En associant une variable
booléenne — par exemple α — à chacune des grandeurs, et en notant α la
valeur 1 de cette grandeur, α la valeur 0 de cette grandeur, on peut construire
une séquence de monômes booléens qui reflète les superpositions de paliers.
On passe à un nouvel élément de la séquence dès que l’une au moins des deux
grandeurs change de palier. Pour l’exemple de la figure 6.1-Bc, on construit la
séquence
α.β, α.β, α.β, α.β, α.β, α.β, α.β, α.β
Il devient intéressant de décrire des machines séquentielles capables de trai-
ter des séquences ainsi construites.
α.β ∨ α.β
Pour la séquence α.β, α.β, α.β, α.β, α.β, α.β, α.β, α.β, la séquence
de sortie est : γ, γ, γ, γ, γ, γ, γ, γ, γ.
l’une des grandeurs est l’horloge de l’autre. La grandeur choisie comme hor-
loge définit un découpage de l’axe du temps qui permet d’échantillonner l’autre
grandeur. Ce découpage n’est pas nécessairement régulier en temps physique ;
l’axe du temps sous-jacent n’est pas découpé en intervalles de tailles égales,
quoique ce soit généralement le cas avec des horloges régulées par des quartz.
En interprétation synchrone, on a donc toujours au moins deux grandeurs.
Notons d’ailleurs que synchrone signifie littéralement qui partage le même
temps, et qu’il faut être au moins deux pour partager quelque chose. Deux
grandeurs seront dites synchrones si elles sont échantillonnées sur la même
horloge, asynchrones sinon.
A partir d’une grandeur qui sert d’horloge et d’une ou plusieurs autres
grandeurs, on fabrique une séquence d’entrées de la machine séquentielle en
créant un élément de séquence par front d’horloge : c’est un monôme qui décrit
le niveau des autres grandeurs à l’instant de ce front.
Nous verrons qu’une machine séquentielle peut être réalisée par un circuit
séquentiel synchrone (Cf. Chapitres 10 et 11). Une horloge détermine alors
les instants auxquels la machine change d’état. Un processeur peut être vu
comme une machine séquentielle synchrone cadencée elle-aussi par son hor-
loge (Cf. Chapitre 14). Il existe aussi des réalisations, dont des processeurs,
asynchrones. Nous n’étudions pas cette technique dans ce livre.
Temps
S2
S1
S0
H
t1 t2
3. Problèmes de synchronisation
Nous avons envisagé jusqu’ici le cas d’un dispositif informatique connecté
à un environnement physique dont il doit échantillonner les grandeurs.
Si l’on s’intéresse à plusieurs dispositifs informatiques, on peut considérer
chacun comme l’environnement de l’autre : les sorties de l’un peuvent être les
entrées de l’autre. Pour étudier les problèmes de synchronisation entre systèmes
informatiques, on suppose que les deux systèmes sont décrits par des machines
séquentielles, et que les entrées de l’un peuvent être les sorties de l’autre.
128 Temps, données temporelles et synchronisation
a) b)
t0
δ1 δ2
Pour éviter les deux cas de fonctionnement incorrect décrits par la figure 6.6,
on doit assurer que :
1. le récepteur ne peut pas lire deux fois la donnée V sans avoir été prévenu
par l’émetteur d’un changement entre temps ;
2. l’émetteur ne peut pas modifier la valeur de la donnée (c’est-à-dire
émettre deux valeurs différentes) à moins d’avoir été prévenu par
le récepteur entre temps que la première valeur a effectivement été
consommée.
On introduit à cet effet deux signaux de synchronisation E prêt et R prêt.
E prêt est produit par l’émetteur et consommé par le récepteur. R prêt est
produit par le récepteur et consommé par l’émetteur. L’idée est d’assurer la
synchronisation par un dialogue entre l’émetteur (E) et le récepteur (R), de la
forme suivante : E est responsable de la production des valeurs V, et prévient
R de l’apparition d’une nouvelle valeur — c’est le signal E prêt ; R attend
d’être ainsi prévenu pour consommer la valeur présente sur le fil ; il envoie
ensuite à E un acquittement de lecture — c’est le signal R prêt ; lorsqu’il reçoit
l’aquittement de lecture en provenance de R, E peut procéder à la production
d’une nouvelle valeur.
d rp d ep
E1 p ep R1 p rp
t3 t1
d rp d rp
d ep d ep
t4 t2
E2 p ep R2 p rp
d rp d ep
He
p ep
d ep
p rp
d rp
Hr
Temps
Fig. 6.8 – Comportement temporel des signaux dans un protocole poignée de mains.
On a représenté : la donnée V dont les valeurs sont émises par l’émetteur,
sur son horloge H e ; l’horloge du récepteur H r ; les signaux logiques p ep,
d ep (resp. d rp, p rp) qui correspondent à l’échantillonnage du signal E prêt
(resp. R prêt) sur les horloges de l’émetteur et du récepteur. Les courbes
pointillées grasses terminées par une flèche illustrent des relations de cause à
effet, déductibles du fonctionnement temporel de l’émetteur et du récepteur.
Les lignes obliques en trait plein, sans flèche, illustrent les décalages temporels
entre la production d’un signal, c’est-à-dire son échantillonnage sur l’horloge
du producteur, et la détection de ce signal, c’est-à-dire son échantillonnage
sur l’horloge du consommateur.
4. Un exemple : la machine à café 133
s1
s1h
s1hf
sortie
s1hf obtenu par détection des fronts montants de s1h ; une sortie sortie de la
machine séquentielle.
Il est nécessaire de détecter les fronts de s1h afin de fournir en entrée du
contrôleur un signal logique qui indique l’insertion d’une pièce pendant au
plus une période d’horloge. En effet la machine séquentielle qui représente le
contrôleur change d’état à chaque période d’horloge, et risquerait sinon d’uti-
liser plusieurs fois le même signal pour compter une pièce de 1F. Nous verrons
au chapitre 9, paragraphe 1.2.4, un dispositif matériel capable de réaliser cette
détection de fronts.
Si l’entrée s1 fait passer dans un état où la sortie sortie est active, le signal
logique correspondant à cette sortie est vrai dès la période d’horloge qui suit
le front montant de s1h et le reste pendant toutes les périodes d’horloge où la
machine séquentielle est dans le même état.
Deuxième partie
Techniques de
l’algorithmique matérielle
Chapitre 7
B bore C carbone
Si silicium P phosphore
Ga gallium Ge germanium As arsenic
Le carbone, le silicium et le germanium ont 4 électrons au niveau d’énergie
le plus élevé, le bore et le gallium en ont 3, le phosphore et l’arsenic 5.
1.1.2 Cristaux
Les atomes d’un corps sont liés entre eux plus ou moins fortement et
peuvent se disposer les uns par rapport aux autres selon des structures
régulières : les cristaux. Le diamant et le graphite sont 2 organisations phy-
siques différentes du même élément chimique carbone. De même il existe des
variétés de silicium monocristallin et polycristallin qui sont obtenues par des
procédés de fabrication différents.
Grille Grille
A B
L
A B
N N x
y N N
substrat P
substrat P
Négativement. Ces deux zones sont espacées d’une distance L. La zone fai-
blement dopée P est nommée substrat. Sur la zone rectangulaire entre les deux
zones dopées, on fait croı̂tre du dioxyde de silicium : le verre (isolant). Au-
dessus du verre on dépose du silicium (polycristallin) et on le dope aussi.
Remarque : La réalité de fabrication est différente : en fait, le dopage du
silicium monocristallin du substrat et celui du silicium polycristallin au-dessus
de l’oxyde pourraient être simultanés : la couche de dioxyde de silicium bloque
la diffusion.
On obtient ainsi deux sandwiches. L’un vertical :
Conducteur – Isolant – Semi-conducteur
et l’autre horizontal :
Semi-conducteur dopé – Semi-conducteur – Semi-conducteur dopé.
Le premier est à l’origine du nom Métal Oxyde Semi-conducteur. Sur la
figure 7.1, les zones dopées du substrat sont notées A et B. On appelle grille la
zone de silicium polycristallin dopé. L’isolant est sous la grille. Les deux zones
A et B sont ici supposées rectangulaires pour faciliter le dessin. La distance L
entre les deux zones est caractéristique d’une technologie de réalisation. Si le
journal annonce la sortie d’un nouveau circuit en technologie 0,17 micron, cela
donne la distance L pour les transistors.
source drain
A=0V B A=0V B A=0V B
Grille Grille Grille
....
.. ::::::::::::::::::::N
::::
N N N : :N N : : : : : : :::::::::::
P P P
Tension de grille = 0 V Tension de grille = 0.5 V Tension de grille = 5 V
face, tout se passe alors comme s’il existait sous la grille un canal drain-source
de faible profondeur, artificiellement dopé négativement par l’accumulation
d’électrons due au champ électrique grille-substrat. Ce canal est conducteur et
un courant peut y circuler. L’intensité du courant est soumise à la loi d’Ohm :
la résistance du canal entre source (A) et drain (B) est fonction de la longueur
et de la section du canal mais aussi de la résistivité du semi-conducteur obtenu.
Cette résistivité diminue à mesure que la différence de potentiel entre
la grille et le substrat augmente. Le transistor fonctionne donc comme une
résistance commandée par la différence de potentiel grille-substrat.
Cet effet de conduction dû à un champ électrique a donné le nom de tran-
sistor à effet de champ.
Une modélisation plus fine du transistor met en évidence une limite du
transistor : la tension du drain et la source doit être inférieure à celle de la
grille faute de quoi le canal ne peut se former. Nous appellerons V gsth la
différence de potentiel minimale entre grille et source nécessaire à la formation
du canal.
La figure 7.2, dans laquelle les petits points représentent des électrons,
suggère la formation du canal.
Par rapport au substrat la grille du transistor se comporte comme une
capacité. Quand la capacité est chargée, elle est au potentiel d’alimentation,
quand elle ne l’est pas, la grille est au potentiel de la masse.
Accroissement
Maintien
5V
0V
entrée
Diminution
e s e s e s e s e s e s
e s1 s2
4 inverseurs en série 2 inverseurs rétro-couplés
Vs Vs Vs
5 5 5
1 1 1
1 5 Ve 1 5 Ve 1 5 Ve
Accroissement Maintien Diminution
dN sN dP sP alim.
masse
à canal N à canal P sN sP
dN dP
sortie
Fig. 7.6 – Schéma conventionnel des transistors MOS et de l’inverseur CMOS. d,g,s sont
respectivement les Drains, Grilles et Sources. N et P désignant les transistors
N et P.
sion de référence, ce qui donne une bonne tolérance aux parasites ([CDLS86]).
Nous donnons ici deux solutions : la principale est la technologie à base
de transistors MOS Complémentaires (Complementary MOS). La figure 7.6
donne la structure interne de l’inverseur. En technologie CMOS, l’inverseur
est obtenu en connectant un transistor N et un transistor P en série entre la
masse et l’alimentation. L’entrée est la tension de grille commune aux deux
transistors et la sortie est le point intermédiaire entre les deux. Les substrats
respectifs des deux transistors N et P sont à la masse et à l’alimentation.
Si l’on considère les différentes valeurs possibles pour la tension d’entrée
V e, on obtient le tableau de la figure 7.7, où RN désigne la résistance du
transistor à canal N, RP désigne la résistance du transistor à canal P, V s
désigne la tension de sortie, égale, dans tous les cas, à Vref × RN /(RP + RN )
ou Vref × 1/(1 + RP /RN ), où Vref désigne la tension d’alimentation.
Une autre solution est de remplacer le transistor P par une résistance, mais
la courbe de transfert est moins intéressante. Le principal avantage est une
plus grande simplicité (Inverseur NMOS). On utilisera dans le livre certaines
portes basées sur ce principe.
Il existe d’autres organisations d’inverseurs. Beaucoup des circuits de petite
échelle d’intégration (quelques centaines de transistors par puce) sont dans
une technique nommée Transistor Transistor Logic TTL. Mais la plupart des
circuits de très grande échelle d’intégration sont aujourd’hui en CMOS.
148 De l’électron aux dispositifs logiques
4. Circuits logiques
Nous allons étudier dans la suite différents assemblages de transistors
réalisant des fonctions booléennes. Ces assemblages seront classés en deux
catégories :
– Les assemblages qui ne mémorisent pas l’information, que l’on nomme cir-
cuits combinatoires,
– Les assemblages qui mémorisent de l’information, que l’on nomme circuits
séquentiels.
Les différences entre ces deux types de circuits sont difficiles à comprendre.
On y revient dans la suite du livre. Chacune des deux familles fait l’objet d’un
chapitre entier (Cf. Chapitres 8 et 10).
P
E S
E=0V
P
S=5V
Charge
E=5V
P N
S=0V
Décharge
N C Chargée
E S
C Déchargée
N
E1
E2
E1 E1
E3
E S
E2 E2
S
S
C c1 c2 c3
e
e1
s e s
A B s
e2
e s
e3
C e s
A B Aiguillage 3 voies vers 1
Commutateur C
- structure interne e s
e s e s
- schéma simplifié
C
Porte de sortie 3 états e s
- structure interne
- schéma simplifié
Si les deux inverseurs ont des entrées différentes, on se trouve avec deux
montages de ponts diviseurs de résistances en parallèle. Si les deux inverseurs
ont des résistances très proches, la tension de sortie est autour de 2,5 volts.
Si l’un des deux inverseurs a des transistors dont les résistances sont très
différentes de l’autre, un des deux inverseurs peut prendre l’avantage, la ten-
sion de sortie prenant des valeurs de 2 ou 3 volts, par exemple. Ce type de
fonctionnement échappe évidemment au domaine des circuits combinatoires
bien construits...
Dans certaines technologies le pont diviseur obtenu laisse passer beaucoup
de courant, ce qui produit de l’énergie thermique. C’est le court-circuit.
4.1.7 L’aiguillage
Observons la figure 7.10. Dans l’aiguillage 3 voies vers 1 réalisé à base de 3
commutateurs et de 3 inverseurs, on dit que la sortie s constitue une connexion
de type bus. les complémentaires des trois signaux e1, e2, e3 peuvent être
envoyés sur le bus. La sortie s est décrite Figure 7.11. Cette sortie est parfois
indéfinie.
Le cas indéfini est complexe ; il y a deux sous-cas. Si l’on trouve deux
ou trois sorties égales sur le bus, cela ne pose pas de problème, et s prend
cette valeur. Si l’on trouve des sorties différentes, selon les forces respectives
des inverseurs qui traitent e1, e2 et e3, s reçoit une valeur non booléenne. Ce
montage ne doit donc s’utiliser qu’avec la garantie que seulement l’un parmi
c1, c2, c3 vaut 1.
OE e
e s
OE s s
e
OE
(a) (b)
OE OE OE
e s e s e s
(c)
A C
B D
4.2.2 Le bistable
Observons la figure 7.14-a. Le montage de deux inverseurs, chacun ayant
comme entrée la sortie de l’autre, a trois points de fonctionnement :
– l’entrée du premier est au niveau logique 1, sa sortie au niveau logique 0.
– à l’inverse, c’est le deuxième inverseur qui a l’entrée à 1 et la sortie à 0 (ces
deux états sont stables, le montage s’appelle un bistable car il a deux points
de fonctionnement stable).
– l’entrée et la sortie des deux inverseurs sont à 2,5 volts. Cet état est instable,
le moindre parasite sur une des deux connexions est amplifiée et le système
tombe dans un des deux états stables. C’est pour cela qu’on a choisi des
inverseurs de type Accroissement (on parle souvent de méta-stabilité pour
cet état).
Il reste un problème : ces deux états sont tellement stables qu’on ne voit pas
comment y piéger une nouvelle valeur. Résoudre ce problème permet de réaliser
une mémoire vive de 1 mot de 1 bit.
F1 F2 F3
F E
e s e s
e s e s C1 C2
s e V1 s e V2
Bistable Bistable avec forçage Points de mémorisation dynamique
(a) (b) (c)
4.2.5 L’oscillateur
4.2.6 Le monostable
Lors de l’appui sur le bouton Reset d’un ordinateur, une impulsion est
générée, puis elle disparaı̂t. Un montage comme celui de la figure 7.15-b assure
cette fonctionnalité.
156 De l’électron aux dispositifs logiques
R
R C R C
Clock Vreset
(a) (b)
Fig. 7.15 – (a) Oscillateur délivrant une horloge. (b) Système de réinitialisation délivrant
une tension de Reset.
lumière
A A
B B
prod photo
2 : après enlèvement 3 après enlèvement
du produit photo du produit A à
A non illuminé découvert
B
1 : flashage
+ + + + + - - - -
, , , , . . . .
x’ y’ + + + + + - - - -
, , , , . . . .
+ + + + + - - - -
, , , , . . . .
Caisson P Caisson N
Coupe xy
Caisson P Caisson N
Caisson P
Coupe x’y’
A B C
' '
(
'
(
'
(
'
(
'
(
'
(
e '
(
'
(
'
(
'
(
'
(
e
1 1 1 1 1 / / / / /
2 2 2 2 2 0 0 0 0
)*
' ' ' ' ' ' ' ' ' ' ' '
( ( ( ( ( ( ( ( ( ( (
% % % % %
1 1 1 1 1 / / / / /
2 2 2 2 2 0 0 0 0
% % % % %
1 1 1 1 1 / / / / /
2 2 2 2 2 0 0 0 0
% % % % %
1 1 1 1 1 / / / / /
2 2 2 2 2 0 0 0 0
% % % % %
1 1 1 1 1 / / / / /
2 2 2 2 2 0 0 0 0
s s
a V a V
Coupe xy
$ $ $ $ $ $ $ $ $ $ $
$ $ $ $ $ $ $ $ $ $ $
# # # # # # # # # # # #
!"
# # # # # # # # # # # #
D E F
Légende des masques Schéma électrique
e entrée
DifP
DifN
Sipol a masse
V Vref
Alu1
Alu2
C1
C2
s sortie
Contacts
Fig. 7.17 – Les masques intervenant dans la technologie CMOS. DifP est le masque de
la zone diffusée P, DifN de la zone diffusée N, Sipol est le masque du silicium
polycristallin, Alu1 et Alu2 sont les deux niveaux d’aluminium, C1 et C2 sont
les deux niveaux de contacts sous l’aluminium. Les masques sont cumulés
dans les différentes parties de la figure. La vue en coupe après l’opération est
selon la ligne xy ou la ligne x’y’. Dans les vues en coupe l’oxyde n’est pas
coloré.
5. Fabrication des dispositifs 159
masques.
Décrivons les opérations principales :
1. La première consiste à doper, faiblement, un caisson P pour y réaliser des
transistors N. Le caisson P existe sous toute la zone des transistors N.
Il y a de même un caisson N pour les transistors P. Cette étape utilise
un premier masque. Puis on délimite à l’aide d’un deuxième masque une
zone active comprenant l’ensemble des zones qui seront diffusées et les
transistors. Des points de contacts entre l’alimentation, ou la masse, et
le subtrat, ou le caisson, sont aussi dans les zones actives. Ce sont les
points de polarisation. A l’extérieur de la zone active se trouve un oxyde
épais de silicium (ces deux masques caisson et zone active sont partie A).
2. Puis on délimite la zone de silicium polycristallin, qui est au-dessus d’une
couche d’oxyde mince. Cette zone est l’entrée de l’inverseur. (partie B,
où le caisson n’apparaı̂t plus).
3. Puis deux masques marquent les zones diffusées N ou P. Cette diffusion
ne passe pas à travers l’oxyde. Elle ne se fait pas, donc ni sous l’oxyde
mince, laissant la place aux canaux des transistors, ni sous l’oxyde épais
hors de la zone active (partie C).
4. Une nouvelle couche d’oxyde épais est ajoutée, dans laquelle on délimite
des trous de contacts selon un nouveau masque (partie D).
5. Des connexions d’aluminium sont gravées entre différentes parties des
différents transistors. L’alimentation, à droite sur la figure, relie le point
de polarisation du substrat N, et un côté des transistors P. La masse, à
gauche sur la figure, relie le point de polarisation du caisson P et un côté
des transistors N. Une autre connexion d’aluminium, au centre, relie le
transistor N, le transistor P et la sortie de l’inverseur. Des connexions
d’aluminium peuvent aussi servir à relier des sorties d’inverseurs ou de
portes à des entrées d’autres portes (partie E).
6. Si nécessaire, on introduit une nouvelle couche d’oxyde épais, percée de
contacts, et une nouvelle couche d’aluminium (partie F). On peut trouver
ainsi 3 ou 4 niveaux d’aluminium.
Puis l’ensemble du circuit est recouvert d’un oxyde de protection.
Fig. 7.18 – Masque du niveau d’aluminium d’un petit morceau de circuit (une centaine
de transistors). On remarque une répétitivité du motif.
certains cas de les simplifier. Les diverses simplifications peuvent porter soit sur
la fabrication proprement dite, soit sur le dessin du circuit. La simplification
vise à apporter un gain soit dans la surface du circuit, soit dans sa vitesse
de fonctionnement, soit dans la difficulté de conception, soit dans sa facilité à
réaliser plusieurs fonctions. La surface est souvent un paramètre critique : plus
le circuit est grand, plus il risque d’y avoir des défauts de fabrication, donc
plus le rendement de fabrication est faible.
Des méthodes visant à simplifier la conception ou la fabrication sont ex-
posées ci-après.
6. Exercices
La ”logique” voudrait qu’en permutant les positions des transistors N et
P dans l’inverseur et la porte NOR, on obtienne respectivement la fonction
identique (S=E) et une porte OR. Les transistors à canal N auront alors leur
drain connecté à l’alimentation et leur source connectée à la sortie. En pratique
le fonctionnement de ce type de porte n’est pas satisfaisant.
Pourquoi ?
Il faut raisonner de façon plus approfondie que 1 ou 0 logique et considérer
les tensions V alimentation - V gsth . Considérons le cas où l’entrée et la sortie
de la porte sont au 1 logique. La tension de sortie devrait être égale à la tension
d’alimentation. Sachant qu’une différence de potentiel minimale grille-source
V gsth est indispensable à la formation du canal et la tension de grille atteint
au mieux la tension d’alimentation, la tension de sortie ne pourra dépasser
V alimentation - V gsth . Le même raisonnement appliqué aux transistors à
canal P et au 0 logique montre que la sortie ne peut descendre en dessous
de V gsth . En résumé, les transistors MOS à canal N (respectivement P) ne
6. Exercices 163
Circuits combinatoires
Alimentation Masse
e0
F s0 F : 2 entrées , 1 sortie
e1 G : 3 entrées, 2 sorties
F s1
s2 Circuit global :
e2 G 4 entrées, 4 sorties
e3 s3
entrées sorties
Fig. 8.1 – Un exemple de circuit combinatoire. Les carrés F et G sont des portes ou des
circuits combinatoires.
1 logique
e0 0 logique
e1
5 Volts
s0 observé 0 Volt
e1 e1 e1 a a
s s s s b s b s
e c
e
e3 e2 e2 d c
1 2 3 4 5 6
NAND à 8 entrées fait l’affaire. Mais pour une fonction à plus de 13 entrées
c’est moins simple.
L’exercice E8.15 donne une idée de solution à ce problème. Dans d’autres
technologies les portes NOR ne peuvent avoir que 2 ou 3 entrées et les portes
NAND que 2, 3 ou 4. Dans certains cas, plusieurs technologies peuvent interve-
nir dans un même équipement matériel comportant plusieurs puces. Une puce
peut ne contenir que des portes à au plus 4 entrées alors que la puce voisine a
des portes à 20 ou 2000 entrées.
a b c d
e f g
NAND soit de NOR. Ces circuits sont optimaux en terme de nombre d’étages.
a b re rs a b re rs
maj(a, b, re ) maj(a, b, re )
0 0 0 0 1 0 0 0
0 0 1 0 1 0 1 1
0 1 0 0 1 1 0 1
0 1 1 1 1 1 1 1
On obtient l’expression optimisée :
a a
b b
re re
s s
Fig. 8.5 – Réalisation de la fonction majorité en portes NAND. Sur le schéma, deux
conventions usuelles sont présentées : avec l’une, les points noirs représentent
des connexions entre un fil horizontal et un fil vertical ; avec l’autre, un trou
dans le fil vertical permet de mettre en évidence la non-connexion.
Fig. 8.6 – Tables de vérité des encodeurs et décodeurs. Les x et les Φ indiquent une
valeur non pertinente respectivement en entrée ou en sortie.
x3 a a
b
x2 c f b
d g
x1 e
f e c
x0 g d
x3 x2 x1 x0
a b c d e f g
a b c d e f g
Fig. 8.8 – Description symbolique des PLA réalisant le codage pour un afficheur 7 seg-
ments. A gauche minimisation de chacune des fonctions, indépendamment les
unes des autres, à droite, minimisation globale.
176 Circuits combinatoires
entre 0 et 15. Il délivre 7 sorties activant 7 segments d’un afficheur. Les 7 seg-
ments se nomment a, b, c, d, e, f et g. Ils sont disposés comme sur la figure 8.7.
Les chiffres hexadécimaux sont affichés comme indiqué. La fonction du circuit
est de transcoder entre le code binaire des nombres et le code en segments
allumés et segments éteints.
On cherche à exprimer chacune des 7 fonctions booléennes a, . . ., g en
fonction de x3 , x2 , x1 , x0 . Par exemple,
x3 x2 x1 x0 x3 x2 x1 x0
c1
c1 11 10 01 00 c0
c0
e1 e0
s
1 0
c
s s
Fig. 8.9 – Représentation symbolique des multiplexeurs 4 voies vers 1 et 2 voies vers 1.
Structure interne du multiplexeur 4 voies vers 1.
2.4 Multiplexeurs
2.4.1 Les circuits existants
Un circuit combinatoire est d’usage fréquent : le multiplexeur. Il réalise la
sélection parmi 2N entrées de données. Celle des entrées sélectionnée est celle
dont le numéro est donné sur les N bits de commande. Le nombre d’entrées
de commande du multiplexeur est le logarithme à base 2 du nombre de bits de
données.
Ainsi pour 2 bits de commande c1 et c0 et 4 bits de donnée x3, x2, x1, x0
la sortie s est décrite par l’équation logique :
2) Nous avons, avec l’aide des auteurs d’un logiciel de synthèse logique,
donné la table de vérité complète de ce circuit à l’outil. Cela représente un
peu moins de 400 lignes de 10 bits. Elles peuvent être obtenues par un pro-
gramme. Ce logiciel a travaillé en aveugle uniquement à partir de ces tables.
Le logiciel cherchait à synthétiser à partir de fonctions à 3 ou 4 entrées. Il a
essayé de minimiser le nombre total de blocs. Il a par ailleurs essayé de regrou-
per des fonctions qui utilisaient les mêmes variables ou les mêmes résultats
intermédiaires. Il a de plus cherché à minimiser le nombre de niveaux logiques
total entre les entrées et les sorties. Le résultat est celui de la partie 1 de
la figure 8.10. Par exemple le bloc représenté en grisé reçoit les 4 entrées de
numéro 7, 5, 3 et 2 et délivre 3 sorties, chacune étant utilisée dans deux blocs.
On dénombre 4 niveaux de blocs entre les entrées et les sorties.
7532 8 7 6 5 4 3 2 1 0
7543864876352 86 864875 642310
9 8 7 6 5 432 1 0
9 8 7 6 5 4 3 2 1 0
Solution 1 Solution 2
an−1 ai b0 a0 b a A B
n n
Add
X X S
additionneur
A B
0 1
r Mux s
rn−1 r0
C
Schéma en tranches Schéma en blocs multiplexeur
An−1 Bn−1 A0 B0
10 10 10 10 Add/Sub
1
0
C V Sn−1 Sn−2 S1 S0
de ce report sortant. Le bit d’oVerflow V est le XOR des deux derniers reports.
Le schéma, en tranches, du circuit est donné figure 8.12.
e1 e0
D2 Décodeur 2 vers 4
s0
e3 s1
D1
s10
e2 s3
s”3 s”0
entrées. Une seule des sorties est à 1. Il est très facile de décrire un tel circuit
récursivement :
– si N vaut 1, le circuit consiste en 1 seul inverseur. Les deux sorties sont
l’entrée et l’entrée complémentée.
– si N est supérieur à 1, on dispose de deux décodeurs à N/2 entrées. Ils
ont chacun 2N/2 sorties. En combinant 2 à 2 dans des portes AND à deux
entrées les sorties des 2 décodeurs, on obtient le décodeur souhaité.
Montrons le passage de 2 entrées à 4 par un exemple (Cf. Figure 8.14).
Un décodeur D1 à 2 entrées e3 e2 délivre les 4 sorties s3 s2 s1 s0 .
Un décodeur D2 à 2 entrées e1 e0 délivre les 4 sorties s3 s2 s1 s0 .
Les équations des sorties du décodeur à 4 entrées sont, pour p compris entre 0
et 15 :
sp = sp div 4 AND sp modulo 4
c’est-à-dire :
s15 = s3 AND s3 s14 = s3 AND s2
jusqu’à s1 = s0 AND s1 s0 = s0 AND s0
année
8 mois
4 jour
5
Déb
>2 bis
9
Les nappes peuvent être interprétées comme des entiers ou des vecteurs de
bits. L’UAL calcule, selon 2 bits de commande com1 com0, la somme de A
et B, la différence de A et B, le quotient de B par 2 ou, sans l’interprétation
entière, le AND (bit à bit) des nappes A et B.
L’UAL comporte un additionneur. En aiguillant les bonnes valeurs sur les
entrées ei , di et la retenue entrante de l’additionneur, on obtient les 3 résultats
arithmétiques en sortie si de l’additionneur (Cf. Figure 8.16). En utilisant
la sous-fonction AND présente dans la fonction majorité de chaque tranche
d’additionneur, on obtient la valeur xi = ei AND di . Un dernier multiplexeur
permet d’obtenir fi égal soit à xi soit à si .
Les sélections des multiplexeurs 1, 2, 3 et 4 de la figure 8.18 peuvent être
obtenues aisément (Cf. Figure 8.17). Il reste à exprimer les commandes de
chaque multiplexeur en fonction de com1 com0.
186 Circuits combinatoires
ai bi
bi+1
1 1 1
0 ci 0 0
2 2 2
3 3 3 3
di ei
fi
Fig. 8.18 – UAL. Les tranches représentées sont la tranche de plus fort poids, une tranche
de rang intermédiaire et la tranche de poids faible. Le remplacement de
certains multiplexeurs par des portes est fait dans la partie droite.
4. Etude de cas
Certains circuits peuvent donner lieu à différentes organisations car l’ana-
lyse de leur décomposition n’est pas unique. Nous montrons ici un exemple
d’un tel circuit. Un exemple analogue est proposé en exercice E8.18.
x0
P1 P0
P2 P1 P0 2
1
4
3
P3
P7 P1 P0
Fig. 8.19 – Différentes solutions pour la réalisation de l’incrémenteur. Les portes XOR
ne sont pas dessinées dans la solution 4
chaque bloc, il faut dessiner le dernier étage de portes réalisant les AND avec
le dernier produit de l’étage précédent.
Dans cette solution aussi il conviendrait de remplacer les cascades de AND
par des cascades de NAND et de NOR, alternativement. La mise au point de
cette solution est un excellent exercice.
5. Exercices
E8.13 : De toutes les couleurs
Reprendre les codes des couleurs dans le début du chapitre 3. Pour les couleurs
codées dans les deux codes, concevoir le circuit combinatoire qui transcode dans
un sens, dans l’autre.
(a + b).(c + d) = a + b + c + d
En déduire le schéma d’une fonction NOR à 8 entrées n’utilisant que des
NAND ou NOR à 2 entrées. Penser à une organisation arborescente.
De même3 donner le schéma d’une fonction NAND à 8 entrées n’utilisant
que des NAND et NOR à 2 entrées.
Généralisation : donner la règle générale permettant de réaliser toute fonc-
tion AND, OR, NAND ou NOR à nombre quelconque d’entrées en se servant de
NAND à au plus 4 entrées, de NOR à au plus 3 entrées et d’inverseurs.
E8.16 : Multiplieur
Reprendre la technique de multiplication des naturels dans le chapitre 3. Etu-
dier le circuit de multiplication. Au lieu d’une itération en ligne, il faut penser
à une itération en matrice. Il est aussi possible de donner une description
récursive de la solution.
E8.18 : Un seul 1
Soit une nappe de N fils xN −1 , xN −2 , . . . , x1 , x0 . Ce sont les entrées d’un circuit
combinatoire C. La sortie S vaut 1 si et seulement si un seul des xi vaut 1.
Nous allons esquisser 5 solutions à ce problème.
– Idée 1 (fonctionne bien pour N petit) : faire la table de vérité de S, donner
l’équation de S, en déduire le circuit.
– Idée 2 : concevoir un circuit C’, n’ayant que N − 1 entrées et deux sorties
Z et T. Z vaut 1 si aucune des entrées ne vaut 1. T vaut 1 si une seule
des entrées vaut 1. Concevoir un circuit C” qui, combiné avec C’ donne un
circuit ayant même comportement que C’, mais N entrées. Construire C
comme circuit itératif par mise en cascade de circuits C”. Résoudre le cas
particulier du premier étage.
– Idée 3 : supposer que N est une puissance de 2. Supposer que l’on sait faire
un circuit C’ à N/2 entrées. C’ a deux sorties Z et T. Z vaut 1 si aucune
des entrées ne vaut 1. T vaut 1 si une seule des entrées vaut 1. Concevoir
un circuit C” qui combine les quatre sorties des deux circuits C’ et délivre
deux sorties Z et T. Construire C comme circuit récursif par mise en arbre
de circuits C”. Résoudre le cas particulier du premier étage.
3
Après être allé au NOR, il faut qu’on pense à faire NAND (G. Brassens)
190 Circuits combinatoires
Eléments de mémorisation
R
Q1
R Q
S Q
S Q2
(a) (b) (c)
Fig. 9.1 – a) le bistable b) la bascule RS réalisée avec des portes NOR c) le symbole
logique de la bascule RS
S
Q1
Q2
temps
Fig. 9.2 – Chronogramme du comportement logique idéal d’une bascule RS. Les poin-
tillés représentent les instants de changement de Q1 et Q2.
S R Q Q
1 1 Q Q Q = R.Q.S = R.Q + S
1 0 0 1 Q = S.Q.R = S.Q + R
0 1 1 0
0 0 1 1
1. Points de mémorisation de bits : bascules et registres 195
1.1.2 Verrou
Un verrou (Cf. Figure 9.4) possède une entrée de donnée D (pour Data),
qui est la valeur à mémoriser, et une entrée de commande En (pour Enable).
Lorsque l’entrée En est active (En=1), le verrou est dit transparent et sa sortie
Q est égale à la valeur de l’entrée D après un petit délai appelé temps de
traversée du verrou. Lorsque En est à 0, le montage est équivalent à un bistable.
La sortie Q est figée et sa valeur est celle de l’entrée D au moment du front
descendant de En. La définition équationnelle du verrou D est : Q = En.D +
En. Q. Le chronogramme de la figure 9.3 illustre ce comportement.
Le verrou peut être réalisé de plusieurs façons, par exemple à partir d’un bis-
table en intercalant un multiplexeur entre les deux inverseurs (Cf. Figure 9.4-
a). L’équation déduite de la figure est : Q = En.D + En.Q. On retrouve là
l’équation du verrou en notant que Q=Q.
Un autre montage peut être envisagé en rebouclant directement la sortie
du multiplexeur sur son entrée. En effet, la réalisation d’un multiplexeur de-
mande l’utilisation de portes, induisant ainsi un délai de commutation lors-
qu’on effectue le rebouclage de la sortie sur l’entrée. Il est donc possible
de ne pas intercaler d’inverseurs (en nombre pair) entre la sortie du multi-
plexeur et l’entrée. Dans la figure 9.4-d, on montre une réalisation de ver-
rou à partir d’un multiplexeur, lui-même réalisé à partir de portes NAND.
Remarquons que nous retrouvons cette réalisation à partir de l’équation :
Q’ = En.D + En.Q = En.D.En.Q.
Nous proposons une dernière réalisation d’un verrou à partir d’une bascule
RS. Nous l’obtenons en transformant l’équation précédente :
En
Q
temps
D
R Q
En
10 En S Q
D (b)
Q
(a)
En
D
Q
D Q
En
(c) (d)
Fig. 9.4 – Trois réalisations d’un verrou de type D et son symbole logique. a) réalisation
d’un verrou à partir d’un bistable et d’un multiplexeur, b) réalisation d’un
verrou à partir d’une bascule RS, c) symbole logique d’un verrou, d) réalisation
d’un verrou à partir d’un multiplexeur seul (en gris est représenté l’intérieur
du multiplexeur).
1. Points de mémorisation de bits : bascules et registres 197
e instants fixés
x’ par H i0 i1 i2 i3 i4
ADD x x0 x1 x2 x3 x4
x
e 1 1 3 7
e (impair)
ADD
Qp Dp
En
H
Q0 D0
(a) Réalisation de l’affectation x’ ←− x + e transparent
x est représenté sur p booléens 1
mémorisés dans p verrous (c) H=1 : verrou transparent
D Q
D1 Q1 D2 Q2
En1 En2
Q1
R1 Q1
H Q Q
S1 Q1 R3 Q3 D
Q Q
R2 S3 Q3 H
Q2
D Q2
S2
Fig. 9.8 – Une réalisation de la bascule de type D à front descendant et son symbole
logique
200 Eléments de mémorisation
S2 = D (1) S1 = H (2)
Q1 = S1.(Q1 + R1) (3) R2 = H + Q1 (4)
R3 = Q1 (5) Q2 = R2(Q2 + S2) (6)
S3 = Q2 (7) Q2 = S2(Q2 + R2) (8)
R1 = Q2 (9) Q3 = R3(Q3 + S3) (10)
Q3 = S3(Q3 + R3) (11)
Nous allons montrer que la sortie ne change pas entre deux fronts descen-
dants de H.
Nous faisons l’hypothèse que D reste stable pendant que H passe de 1 à 0.
Considérons l’état initial H=1, qui précède le front descendant de H. En
appliquant les équations, nous obtenons les résultats partiels suivants : S1 =
1, Q1 = 0, R2 = 1, R3 =0 , Q2 = 0, S3 =0 . Donc, la bascule RS3 ne change
pas d’état et la sortie Q3 est stable.
Supposons qu’à l’état initial, on ait en plus D=0. Il en résulte que S2=0,
Q2=1, R1=1. Lors du passage de H à 0, nous obtenons S1=0 d’après (2).
Puisque R1=1 d’après (3), nous obtenons Q1=1. D’où R3=1 . Par ailleurs,
le fait que R2=1 entraı̂ne Q2=0 d’après (4), et donc S3=0 . La sortie de la
bascule Q3 est 0. Ensuite, tant que H reste à 0, Q1 = 1 et R2=1. Il s’ensuit
que R3=1 et S3=0 . La sortie reste à 0. Lorsque H repasse à 1, Q1=0 d’après
(2) et (3), et R2=1 d’après (4). Donc R3 passe à 0 et S3 reste à 0 : la sortie
reste inchangée.
Si à l’état initial D=1, alors S2=1, Q2=0, R1=0. Nous obtenons Q1 = 0.
Comme R2=0 et S2=1, Q2=1. La bascule RS3 est forcée à 1. Par un raison-
nement analogue au cas où D=0 à l’état initial, la sortie Q3 reste stable.
Nous avons montré que, si l’entrée D reste stable pendant que H passe de
1 à 0, la sortie Q3 reste stable jusqu’au front descendant suivant.
Le chronogramme de la figure 9.9 montre l’évolution de la sortie Q de la
bascule à front descendant en fonction de l’entrée D.
Du point de vue de la réalisation, en technologie CMOS, la bascule à front
utilise autant de transistors qu’une bascule maı̂tre-esclave c’est-à-dire deux fois
plus qu’un verrou.
1. Points de mémorisation de bits : bascules et registres 201
S
e
Q1 Q2
H
e
Q1
Q2
D 0 Q D Q
1 D Q
H Ch
Ch H
Fig. 9.11 – Une réalisation d’une bascule avec commande de chargement et son symbole
logique
2. La mémoire : organisation matricielle des points de mémorisation 203
Fig. 9.12 – Mémoire de 2m mots de n bits et signification des signaux SelMem et l/e.
1. Les adresses sont sur m bits et les données sur n bits. Les adresses sont
des adresses de mots de n bits et les accès mémoire sont limités aux seuls
mots de n bits. Le cas général permettant l’accès à des sous-ensembles du
mot mémoire est étudié dans le chapitre 15. L’accès à des sur-ensembles
du mot mémoire, en mode rafale, est étudié dans le paragraphe 4.3 du
présent chapitre.
2. La mémoire : organisation matricielle des points de mémorisation 205
Adresse A1 A2
AccesMem
l/e
T1 T2
La figure 9.13 montre une évolution possible des différents signaux, données
et adresses intervenant lors d’un accès à la mémoire par le processeur.
v v
(a) (b) C1 B1
Cellule
moti
moti
EnD
A0 Q motj
+-
Am−1 motj Sélecteur
EnD
Q
l/e
SelMem
l/e Don
C2 B2
Don SelMem
SelMem
Poids forts
A19
A9
l/e
Sélecteurs
Poids faibles
A8
A0
Données
Par ailleurs, les mémoires dynamiques sont dotées d’un registre interne
de stockage de numéro de ligne (adresses de poids fort), ce qui permet
d’économiser la moitié des broches d’adresse sur le boı̂tier au prix d’un dispo-
sitif externe de multiplexage (commun à tous les boı̂tiers).
L’accès mémoire se déroule en deux temps : le numéro de ligne est en-
voyé le premier et stocké dans un verrou interne. Le temps de décodage et
l’établissement de la connexion entre la ligne sélectionnée et les signaux de co-
lonne est mis à profit pour transmettre la deuxième partie de l’adresse (numéro
de colonne) au boı̂tier. Notons que dans la salve d’accès à différentes colonnes
d’une même ligne, l’étape de sélection et de connexion de la ligne aux colonnes
peut être effectuée en une seule fois en début de salve. Cette optimisation est
applicable à toute suite d’accès mémoire à des adresses ne différant que par les
poids faibles, qui correspondent au numéro de colonne (Cf. Paragraphe 4.3).
A partir de ce principe, certaines mémoires ont été conçues spécialement pour
la réalisation de cartes vidéo (Cf. Paragraphe 4.5).
adresse
mode page r c1 c2 c3 c4
adresse r c1
mode quartet
RAS
CAS
Donnée a b c d
Fig. 9.16 – Chronogrammes décrivant l’accès mémoire en mode rafale, pour une interface
asynchrone
adresse
r c1
mode quartet
RAS
CAS
SelMem
Donnée a b c d
Horloge
Fig. 9.17 – Chronogrammes décrivant l’accès mémoire en mode rafale, pour une interface
synchrone
4. Optimisations et techniques particulières 213
V W W V
mot b
mot a
Circuits séquentiels
la conception du circuit se fait selon des procédés différents (Cf. Chapitre 11).
On est proche ici de l’algorithmique câblée. Deux grands types d’architectures
(organisations matérielles) des circuits séquentiels sont alors employés.
Dans l’un, la partie qui permet de stocker les variables de l’algorithme
et de réaliser les calculs sur ces variables (partie opérative) est séparée de la
partie commandant l’enchaı̂nement de ces opérations (partie contrôle). Ces
deux parties sont des circuits séquentiels.
Dans l’autre type d’architecture, les aspects de contrôle et de calcul sont
mélangés. Ce sont les valeurs des variables (les données) qui contrôlent direc-
tement l’enchaı̂nement des opérations sur celles-ci. On parle d’architecture à
flots de données (Data flow en anglais). Des architectures à flots de données
sont illustrées par des exemples dans ce chapitre. Le cas particulier des orga-
nisation à pipeline est introduit.
La méthode de synthèse basée sur une partie contrôle et une partie opérative
est présentée en détail au chapitre 11.
Ces méthodes de conception de circuits sont aujourd’hui automatisées grâce
à des outils de CAO de circuits. La réalisation se fait à partir des spécifications
des algorithmes dans différents langages. Le plus courant, VHDL (devenu
un standard) permet de décrire des spécifications de circuits séquentiels à
différents niveaux : graphes d’automates d’états fini et algorithmes à base
d’instructions de types divers (itératif, conditionnel . . .).
Nous définissons dans le paragraphe 1. la notion de circuit séquentiel en
précisant son architecture et en décrivant son comportement temporel.
Dans le paragraphe 2. nous étudions en détail les méthodes de réalisation
de circuits séquentiels à partir du graphe explicite d’un automate d’états
fini (Cf. Chapitre 5). Nous détaillons ici deux types de synthèse : câblée et
microprogrammée. Dans le paragraphe 3. nous décrivons deux exemples
de réalisations par flots de données de circuits séquentiels à partir d’un
algorithme. Nous donnons aussi une idée de la notion de pipeline.
entrées entrées
Fonction sorties Fonction sorties
de sortie de sortie
Fonction Fonction
de de
transition transition
bascules
bascules
Etat courant Etat suivant
Fig. 10.1 – Architecture générale d’un circuit réalisant un automate d’états fini.
a) Modèle de Moore ; b) modèle de Mealy.
Calcul de l’état suivant (Cf. Figure 10.2) Supposons ici que le front d’ac-
tivation des bascules du circuit séquentiel soit le front montant de l’horloge.
Soit t-états le délai nécessaire à la stabilisation des circuits combinatoires de
calcul de l’état suivant. Nous avons vu au chapitre 8 que ce délai n’est pas nul.
Soit i-entrées l’instant à partir duquel les entrées sont stables.
Remarque : Le temps de stabilisation de la sortie des bascules n’est pas
nul. On le néglige ici par rapport aux délais de stabilisation des circuits com-
binatoires.
Une première idée est d’échantillonner les entrées sur le front descendant
de clock. Le circuit arrivant dans un nouvel état au front montant et les entrées
sur le front descendant, les sorties des circuits combinatoires calculant l’état
suivant ont alors une demi-période de l’horloge pour se stabiliser. Le chrono-
gramme de la figure 10.2-b montre cette dépendance : la demi-période de clock
doit être supérieure à t-états pour que les entrées des bascules soient stables
au front montant.
Si l’on regarde de plus près, il s’avère que l’on peut anticiper cet
échantillonnage et l’effectuer au même front que le changement d’état. Au
même instant l’état suivant est mémorisé et les entrées sont fournies. En effet
grâce au temps de stabilisation des sorties des circuits combinatoires t-états,
l’arrivée des nouvelles valeurs des entrées n’est pas encore répercutée à l’entrée
des bascules au moment du chargement du nouvel état. La période de clock doit
être supérieure à t-états. Pour une valeur de t-états maximale donnée, on peut
ainsi doubler par rapport à la première solution (Figure 10.2-b) la fréquence
maximale à laquelle l’automate peut évoluer (si on prend des niveaux haut et
bas de l’horloge de même durée). Le chronogramme de la figure 10.2-c montre
cette évolution.
Clock Clock
Temps Temps
Fig. 10.3 – Chronogrammes des sorties d’un automate. a) Cas de Moore ; b) cas de
Mealy.
Les sorties sont stables un temps t-sorties après i-entrées (Cf. Figure 10.3-b).
Il apparaı̂t alors des valeurs transitoires sur les sorties pendant des temps non
négligeables avant leur stabilisation. Ces valeurs transitoires peuvent provoquer
des évolutions non voulues pour un système aval (qui utiliserait les sorties ainsi
produites).
1.3.2 Initialisation
Nous avons vu au chapitre 9 qu’il existe deux sortes d’initialisation des
bascules. L’initialisation asynchrone est effectuée dès la présence de la valeur
d’initialisation sur le signal correspondant. L’initialisation synchrone n’est ef-
fectuée qu’au moment du front d’activation de l’horloge.
Si l’initialisation n’est pas effectuée au moment du front montant de l’hor-
loge l’automate peut passer dans un état indéterminé si les circuits combi-
natoires de calcul de l’état suivant n’ont pas le temps de se stabiliser entre
l’instant d’initialisation et le prochain front montant de l’horloge. La solution
synchrone est donc utilisée de préférence.
Clock1
t-sorties1
t-états1
Clock2
t-états2
Une façon simple pour réaliser ce décalage est de prendre pour clock2 le
complément de clock1. Le décalage est alors d’une demi-période. Pour que les
entrées des bascules des deux automates soient toujours stables au moment du
front d’activation, la période minimale P de l’horloge doit alors vérifier :
– P/2 > t-sorties1 + t-états2 pour que l’état de l’automate 2 soit stable au
moment du front montant de clock2.
– P > t-états1 pour que l’état de l’automate 1 soit stable au moment du front
montant de clock1.
Dans ce cas le décalage de l’évolution de l’état des deux automates est
d’une demi-période de l’horloge.
Il s’avère que l’on peut aussi prendre clock1 égale à clock2. Les entrées des
bascules restent stables si l’on respecte les conditions suivantes sur la période
P de l’horloge :
– P > t-sorties1 + t-états2 pour que l’état de l’automate 2 soit stable au
moment du front montant de clock.
– P > t-états1 pour que l’état de l’automate 1 soit stable au moment du front
montant de clock.
bascules
init clock init clock
Clock
t-sorties1 t-états2
t-sorties2 t-états1
supposons que les deux soient de type Moore. Les deux automates peuvent alors
évoluer à l’aide de la même horloge clock (chronogramme de la figure 10.6).
Nous obtenons alors les conditions suivantes sur la période P de l’horloge :
– P > t-sorties1 + t-états2 pour que l’état de l’automate 2 soit stable au
moment du front montant de clock.
– P > t-sorties2 + t-états1 pour que l’état de l’automate 1 soit stable au
moment du front montant de clock.
R va
Un
va vp
vp
O V
Quatre Deux vp
vp
V va
va
Trois
Etat q1 q2
Vocabulaire s1 s2 Un 0 0
Vocabulaire e R 0 0 Deux 0 1
va 0 O 0 1 Trois 1 0
(a) vp 1 (b) V 1 0 (c) Quatre 1 1
Fig. 10.8 – Codage des entrées, des sorties et des états du système de commande de feu
tricolore.
chrone échantillonnées sur une horloge qui découpe le temps de façon régulière.
deux fois de suite signifie alors pendant deux périodes d’horloge successives.
QD
n n
... d1 ...dn
q1 ...qn Mémorisation de l’état courant
init clock
donne les valeurs des variables codant l’état suivant (d1 , d2 ) en fonction des
variables codant l’état courant (q1 , q2 ) et de l’entrée (e). Ce tableau correspond
à la table de vérité des deux fonctions booléennes d1 , d2 . La figure 10.10 donne
ces tables ainsi que celles correspondant aux sorties. La figure 10.11 décrit le
circuit résultant réalisé à base de portes NAND et d’inverseurs.
état sortie q1 q2 s1 s2
Un R 0 0 0 0
Deux V 0 1 1 0
Trois V 1 0 1 0
Quatre O 1 1 0 1
s1
Fonctions de sortie
d1
DQ
q1
d2
DQ
q2
clock init
Fonctions de transition
Etat q1 q2 q3 q4
Un 1 0 0 0
Deux 0 1 0 0
Trois 0 0 1 0
Quatre 0 0 0 1
Fig. 10.12 – Codage 1 parmi n des états du système de commande de feu tricolore
e
ē D
1 Q1
e
2 2
s1
ē e
s1
3 3
s1
ē
4 4
s2 s2
Fig. 10.13 – Un circuit réalisant un automate avec un codage un parmi n des états
sorties[q]
succ [q]
+1
état-suivant
q+1
Bascules
ROM
q
clock init
Fonction f de
calcul de condition cond [q] (types de condition)
entrées
peut que cela ne soit pas possible, on ajoute alors un état supplémentaire
afin d’obtenir cette configuration.
Calcul de l’état suivant Le code de l’état courant est mémorisé dans des
bascules et fournit une adresse de la mémoire.
Les primitives de calcul de l’état suivant sont de deux types : une
incrémentation (circuit +1 sur la figure 10.14) ; la donnée dans la mémoire
du code de l’état suivant, l’accès à ce code se faisant à l’aide du code de l’état
courant (adresse d’une ligne de la mémoire).
Le fait que chaque état ait au maximum deux états successeurs permet de
limiter la largeur de la mémoire. Quand le code de l’état suivant n’est pas le
code de l’état courant plus 1, son code se trouve dans la mémoire.
Par ailleurs, il faut pouvoir spécifier quelle est la condition permettant de
choisir l’état successeur (dépendant des entrées de l’automate) quand il y en
a deux. Cette condition est aussi spécifiée dans la mémoire et correspond à
certaines sorties de celle-ci. On parle de champ condition.
Soit q le code d’un état, soit succ[q] le code de l’état successeur (qui n’est
pas q + 1) contenu dans la mémoire, soit cond[q] la condition à tester pour
choisir l’état successeur suivant les entrées de l’automate. L’architecture mi-
croprogrammée comporte les circuits permettant de définir le code de l’état
suivant comme suit :
état-suivant(q)= si f (cond[q], entrées) alors succ[q] sinon q+1.
Les codes sont choisis de façon à ce que :
– quand un état de code q ne possède qu’un état successeur, cond [q] spécifie
soit la condition toujours vraie et état-suivant(q) = succ[q], soit toujours
2. Synthèse des automates décrits par leur graphe 231
+1 2 s1 s2
0 0 0 0 0
0 DQ
0 1 1 1 0
1
DQ 0 1 1 1 0
0 0 φ 0 1
1 0
c
AUCUNE rien
Attente
Pièces
s5
s2
fs fs
rien Trop rien
perçu 2F reçus
R,C,B C,B
s2 s1
s1
s5
1F reçu rien
AUCUNE
Etats q1 q2
Vocabulaire de sorties sortie1 sortie2 Attentes Pièces 1 1
AUCUNE 0 0 1F reçu 1 0
R,C,B 1 1 2F reçu 0 1
C,B 0 1 Trop perçu 0 0
Fig. 10.18 – Codage des entrées, des sorties et des états pour la synthèse de l’automate
de contrôle de la machine à café
e1 e2 e3 q1 q2 d1 d2
0 0 0 0 0 0 0 e1 e2 e3 q1 q2 d1 d2
0 0 1 0 0 1 1 0 1 0 1 0 0 0
0 0 0 0 1 0 1 0 0 0 1 1 1 1
0 0 1 0 1 1 1 1 1 0 1 1 0 0
0 0 0 1 0 1 0 1 0 0 1 1 1 0
1 0 0 1 0 0 1 0 1 0 1 1 0 1
1 1 0 1 0 0 0 - - - - - φ φ
init clock U
U0
1
U0 x DIV 2
1
DQ 0 U
0 1
3∗x+1
n
U 0n−1 U 0i U 00
init
D D D clock
Q Q Q
Qn−2 Qi Q0 0
Qi−1
Add cn−1 ci+1 Add ci c1 Add 1
0 Qn−1 Qi+1 Qi Q1
Q0
Fig. 10.21 – Architecture en tranche pour le calcul de la suite de Syracuse par un circuit
à flot de données
Lexique
x : l’entier ≥ 0 donné
u, z, y, résultat : des entiers ≥ 0
Algorithme
u ←− 1
tantque u ≤ x : u ←− 4* u
z ←− u ; y ←− x
tantque u > 1 :
u ←− u DIV 4
z ←− z DIV 2 - u
si z ≤ y :
y ←− y - z
z ←− z + 2 * u
résultat ←− z DIV 2
résultat
U Z Y
div4 u div 4
div2 z div 2 - u
moins
mult2 z + 2*u
plus
moins y-z
test si z ≤ y
Entrées Sorties
R1 C1 R2 C2
Clock
4. Exercices
E10.2 : Compteur
On veut réaliser un compteur. Il délivre en sortie les entiers successifs de 0
à 7 (sur 3 bits). La sortie sur 3 fils (s2 , s1 , s0 ) est incrémentée modulo 8 à
chaque front montant d’une entrée incr. L’initialisation à 0 des sorties se fait
à l’aide du signal init actif à 1. Donner l’automate de Moore correspondant à
ces spécifications. Donner une réalisation câblée de cet automate.
Comment pourrait-on réaliser un circuit équivalent à l’aide d’un circuit
combinatoire calculant une sortie S sur 3 bits égale à une entrée E (sur 3 bits)
plus 1 ? Ce circuit a fait l’objet d’une étude de cas dans le chapitre 8.
Vérifiez en dessinant un chronogramme que le circuit de la figure 10.25 a
le même comportement que les circuits précédents.
incr
ē s=0
s=0
e s=1 e
ē e
Conception de circuits
séquentiels par séparation du
contrôle et des opérations
Acquittements Ordres
Partie contrôle
1. Principe général
La partie opérative (ou PO) offre les ressources (Bus, registres, UAL . . .)
nécessaires à chaque opération sur les différentes variables apparaissant dans
l’algorithme. Mais ce n’est pas elle qui décide de l’opération à effectuer à un
instant donné. Elle envoie des signaux de comptes rendus sur ces calculs à la
partie contrôle.
La partie contrôle (ou PC) gère l’enchaı̂nement des calculs effectués sur
les données au vu des comptes rendus de la PO. Elle génère l’activation des
opérations à un instant donné, en envoyant des signaux de commandes à la
partie opérative. Elle ne modifie pas directement les données. Elle traduit les
primitives de contrôle apparaissant dans l’algorithme.
Les deux parties sont deux circuits séquentiels cadencés sur la même hor-
loge. La figure 11.1 donne l’organisation des signaux entre la PC, la PO et
le monde extérieur. Les communications entre les deux parties se font par
les signaux de commande et de compte-rendu. A chaque front (montant par
exemple) d’horloge :
– des valeurs de commandes sont envoyées à la PO par la PC pour sélectionner
un calcul donné (par exemple : sélection des entrées de l’UAL, opération
effectuée dans l’UAL, . . .).
– des comptes-rendus du calcul effectué (par exemple les indicateurs
arithmétiques d’une UAL) peuvent alors être renvoyés par la PO à la PC
afin de lui permettre de prendre une décision pour le choix du calcul suivant.
Les connexions au monde extérieur tiennent compte de cette spécialisation :
– la PC ne reçoit que des ordres et ne délivre que des acquittements, signalant
la fin de sa mission, par exemple.
– la PO ne reçoit que des données et ne délivre que des données. Les fils
d’entrées et de sorties peuvent dans certains cas être les mêmes (bus bidi-
rectionnel).
2. Notion de partie opérative type 245
Sorties
SelSorties Opération UAL
Bus A
ChR1
SelBusAR1 UAL
R1 Rn
SelBusBR1
SelUAL
horloge
Bus B
ComptesRendus
Bus Résultat
SelEntrées
Entrées
Fig. 11.3 – Sorties correspondant aux microactions pour la P.O type de la figure 11.2
2. Notion de partie opérative type 247
La sortie de chaque registre est connectée aux deux entrées de l’UAL mais
une de ces deux connexions peut être inutile et donc supprimée. Il peut être
intéressant d’initialiser les registres à l’aide du signal d’initialisation (à 1 ou à
0) des bascules plutôt que d’obtenir des valeurs initiales via le bus Entrées.
C1 C C1 C2
2
Décodeur
D1
D1 D2 D3 D4
D2
BUS
D3
S1 S2 S3 S4
D4
BUS
(a) (b)
La liaison des registres à chaque bus est réalisée soit à l’aide de multi-
plexeurs, soit à l’aide de portes trois états entre les sorties des registres et
le bus. Elle nécessite des signaux de commande permettant de déterminer ce
choix. La figure 11.4 montre la réalisation d’un bus supportant 4 entrées D1 ,
D2 , D3 et D4 (qui sont par exemple les sorties de 4 registres) à l’aide de deux
signaux de commande C1 et C2 . Dans le cas d’une réalisation à base de portes
trois états, le bus obtenu est à double sens (contrairement à l’autre cas). Cela
peut être indispensable dans le cas d’entrées/sorties avec l’extérieur. Cette
solution est la plus souvent utilisée.
Sur la figure 11.2, c’est la solution à base de portes trois états qui a été
choisie. Le décodeur délivrant les signaux Si (dans la figure 11.4) n’apparaı̂t
pas sur la figure 11.2. Ces signaux de sélections (Si ) peuvent être directement
délivrés dans chaque état de la PC. Une autre solution consiste a réaliser le
décodeur dans la PC ; le nombre de fils de commande entre la PC et la PO est
alors fortement augmenté.
On peut diminuer le nombre de connexions en diminuant le nombre de
bus au détriment du temps d’exécution de l’algorithme. On peut placer par
exemple un registre tampon supplémentaire en sortie de l’UAL et connecter ce
registre à un bus qui servira en même temps de bus résultat et de bus opérande.
2.4 Entrées/Sorties
Le bus Entrées permet de charger des valeurs depuis l’extérieur dans les
registres. Les signaux SelEntrées et ChRi du registre concerné doivent alors
être actifs et la valeur initiale présente sur le bus Entrées.
Le bus Sorties permet de délivrer à l’extérieur les résultats de l’algorithme.
Il est donc connecté à un des deux bus de sorties des registres de la PO. La
porte trois états activée par SelSorties n’est pas toujours nécessaire.
3. Partie contrôle 249
3. Partie contrôle
Comme décrit au chapitre 5, nous pouvons à partir de l’algorithme obtenir
une machine séquentielle avec actions. Pour des raisons de synchronisation
avec la PO (Cf. Chapitre 10, paragraphe 1.3.3) le modèle de Moore est utilisé.
Cette machine séquentielle avec actions est ensuite transformée en auto-
mate d’états fini en remplaçant les actions apparaissant sur les états par l’af-
fectation des valeurs correspondantes à ces actions, aux signaux de commande
à destination de la PO. Cette étape est détaillée dans les études de cas traitées
250 Conception de circuits séquentiels...
au paragraphe 4. Cet automate peut être ensuite réalisé par du matériel suivant
une des méthodes décrites dans le chapitre 10.
Le problème est de décider quels opérateurs et comptes-rendus de calculs
sont disponibles dans la PO. Chaque calcul et affectation de variable corres-
pondante effectué dans un état de l’automate doit être réalisable en un cycle
d’horloge dans la PO. Chaque condition apparaissant sur les transitions de
l’automate doit être un compte-rendu de l’opérateur utilisé disponible dans
l’état précédent.
3.1 Entrées/sorties
La gestion des entrées et des sorties nécessite une synchronisation avec le
monde extérieur. Le protocole de poignée de mains (Cf. Chapitre 6) peut être
employé pour permettre le chargement ou la sortie de certains registres de la
PO. Ce protocole de poignée de mains peut être adapté au cas par cas suivant
l’environnement dans lequel on doit implanter le circuit à réaliser. Des signaux
de synchronisation nécessaires à ces entrées/sorties sont ajoutés aux signaux
de données. Ils sont reçus par la PC.
Dans le cas d’une entrée le circuit est le récepteur et le monde extérieur est
l’émetteur, et inversement pour une sortie. On reprend le schéma de l’automate
du récepteur et de l’émetteur dans une poignée de mains présenté dans le
chapitre 6. On associe à l’automate de la PC deux états pour chaque acquisition
d’entrée et pour chaque délivrance de sortie (Cf. Figure 11.5). Les entrées sont
échantillonnées sur la même horloge que la PC comme nous l’avons vu dans le
chapitre 10. Dans le cas d’une entrée, le signal PresE correspond au signal de
présence d’une entrée venant de l’extérieur (émetteur prêt). Le signal EPrise
correspond au signal de signification à l’extérieur de la prise en compte de
l’entrée (récepteur non prêt). Bien entendu ce signal est à 0 dans tous les
autres états de l’automate. Dans l’état Chargement de l’entrée les commandes
à destination de la PO sont SelEntrées pour amener la valeur du bus extérieur
à l’entrée des registres et ChRi pour charger le registre voulu.
Dans le cas d’une sortie, le signal PresS correspond au signal de présence
d’une sortie pour l’extérieur (émetteur prêt). Ce signal est à 0 dans tous les
autres états de l’automate. Le signal SPrise permet à l’extérieur de signaler
au circuit qu’il a pris en compte la sortie (récepteur non prêt). Dans l’état
Sortie présente les commandes à destination de la PO sont SelBusARi pour
transmettre le registre voulu sur le bus A et SelSorties pour amener la valeur
du bus A sur le bus de sortie.
Nous pouvons illustrer ce protocole à travers le dialogue d’un circuit avec
une mémoire. Considérons par exemple un processeur effectuant des écritures
(sorties) ou des lectures (entrées) en mémoire. Nous avons expliqué au para-
graphe 2.2 du chapitre 9 comment se déroule un accès mémoire. Dans le cas
où la mémoire est lente par rapport au processeur celui-ci doit attendre lors
d’un accès en lecture ou en écriture que la mémoire lui signale la fin de l’accès.
3. Partie contrôle 251
PresS
EPrise Attente PresE Sortie SPrise
SelSorties
Entrée présente
SelBusARi
PresE SPrise
EPrise
ChRi Chargement PresS SPrise
PresE Attente
SelEntrées de l’entrée
PresE SPrise
(a) (b)
La mémoire délivre alors un signal FinAccès lorsque, soit la donnée à lire est
prête sur le bus, soit la donnée à écrire est effectivement écrite en mémoire.
Le processeur délivre les signaux SelMem et l/e lors d’un accès à la mémoire.
On reprend les automates de la figure 11.5. Pour l’écriture SPrise correspond à
FinAccès, PresS à SelMem et l/e ; pour la lecture PresE correspond à FinAccès,
Eprise à SelMem et l/e.
Nous retrouverons la connexion d’un processeur avec une mémoire dans l’étude
de cas du paragraphe 4.3 et dans le chapitre 14. Nous nous placerons alors dans
le cas simple et idéal où la mémoire est suffisamment rapide pour permettre un
accès en un cycle d’horloge du processeur ; le signal FinAccès n’est alors plus
utile, il est implicite.
Nous retrouverons les aspects de synchronisation au chapitre 15, pour relier
l’ensemble processeur/mémoire avec le monde extérieur.
4. Etudes de cas
4.1 Démarche de conception
Avant de traiter concrètement des exemples précis nous donnons l’ébauche
d’une méthode de conception d’une architecture PC/PO. Les deux premières
étapes apparaissant ci-après sont complètement liées et sont à effectuer simul-
tanément.
Lexique
m,n : des entiers ≥ 0 { m et n étant donnés}
fin : le booléen Vrai
j, k : des entiers ≥ 0
Δ : un entier
Algorithme
Tantque VRAI :
Acquérir(m) ; Acquerir(n) ;
k ←− 0 ; j ←− 0 ; Δ ←− −m ;
tantque j ≤ m : {invariant : 0 ≤ j ≤ m et −2.m ≤ Δ ≤ 0 }
Délivrer (j) ; Délivrer (k) ;
j ←− j+1 ;
Δ ←− Δ + 2.n ; {−2.m + 2.n ≤ Δ ≤ 2.n}
si Δ ≥ 0
k ←− k + 1 ;
Δ ←− Δ − 2.m ; { après cela : −2.m ≤ Δ ≤ 0 }
fin ←− VRAI ;
Délivrer(fin) ;
Acquérir(m)
Délivrer(fin)
Acquérir(n)
Δ ←− −m || j ←− 0 || k ←− 0
ResNeg
m-j
ResNeg
Δ ←− Δ − 2.m Délivrer(j)
ResNeg
Délivrer(k)
k ←− k+1
ResNeg j ←− j +1
Δ ←− Δ + 2.n
Fig. 11.9 – Une machine séquentielle avec actions réalisant l’algorithme de Bresenham
facile de réaliser une UAL qui donne un indicateur signifiant résultat négatif
que résultat négatif ou nul. Pour le prédicat Δ ≥ 0, on a besoin du même
indicateur.
Le calcul d’un prédicat peut nécessiter l’ajout d’un état. Par exemple ici le
calcul de j > m doit être effectué dans un état spécifique alors que le calcul de
Δ ≥ 0 peut être effectué au moment de l’action Δ ←− Δ + 2.n.
On obtient la machine séquentielle avec actions de la figure 11.9.
Sorties
Bus A Op2, Op1, Op0
SelDBusA
SelJBusA
SelKBusA
ChJ ChK ChD ChM ChN
SelMBusB
SelNBusB
UAL
J K D M N
SelUAL
InitJK InitJK
H
Bus B
ResNeg
BUS Résultat
SelEntrées
Entrées
réaliser les opérations suivantes sur ses opérandes A et B : A+1, B−A, A+2*B,
A−2*B et −B. Chacune de ces opérations doit être effectuée dans un état
différent de la machine séquentielle avec actions.
La signification des trois signaux Op2 , Op1 , Op0 de commande des
opérations de l’UAL est donnée dans le tableau de la figure 11.10.
Cette UAL peut être réalisée à l’aide d’un additionneur (Cf. Chapitre 8).
Elle doit générer un bit de signe du résultat (ResNeg).
4.2.5 Assemblage de la PC et de la PO
PresM 1
PresM
SPrise
PresM 2 Chargement de M
SPrise
PresM
16
PresN 3
SPrise PresN
Sortir Fin
PresN 4 Chargement de N
15
PresN
SPrise
5 D ←− −M J ←− 0 K ←− 0
ResNeg
M−J
6
ResNeg
SPrise 7 Sortir J
SPrise
14 D ←− D − 2*M SPrise
8
SPrise
ResNeg SPrise
9 Sortir K
13 K ←− K+1
SPrise
SPrise
ResNeg 10
11 SPrise
12
J ←− J +1
D ←− D+2*N
Fig. 11.13 – Sorties émises dans chaque état de la partie contrôle de la figure 11.12. Les
signaux dont la valeur est 1 ou φ sont précisés et ceux pour lesquels elle
vaut 0 sont omis.
H Init
PresN
Sorties ResNeg PresM
SPrise
Entrées Partie ChJ ChK ChD ChM ChN Partie
SelJBusA SelKBusA EPrise
opérative SelMBusB SelNBusB contrôle
SortieJ
SelDBusA SelSorties
SelUAL SelEntrées SortieK
Op2 Op1 Op0 InitJK SortieFin
Lexique
M : un entier donné
i, j, t : des entiers ≥ 0 ; trouvé, fin : des booléens
mem : un tableau [0..M−1] d’entiers ≥ 0
Algorithme
i ←− 1
tantque i ≤ M−1 :
j ←− i ; t ←− mem[i] ; trouvé ←− FAUX
tantque (j > 0 et non trouvé) :
si mem[j−1] > t alors mem [j] ←− mem[j−1] ; j ←− j−1
sinon trouvé ←− VRAI
mem [j] ←− t ; i ←− i + 1
fin ←− VRAI ;
Délivrer (fin)
0 i ←− 1
2 Délivrer(fin)
ResNul
1 i−M
ResNul
3 j ←− i
j 5
ResNul
ResNul
6 trouvé
ResNul
mem[j] ←− t ResNul
12
7 temp ←− mem[j−1]
i ←− i + 1 13
8 temp - t
ResPos
ResPos
j ←− j − 1 10
L’initialisation de trouvé à FAUX peut être faite dans le même état que
l’affectation t ←− mem[i] en se servant de l’initialisation à 0 du registre trouvé.
Le calcul du prédicat j > 0 et non (trouvé) se fait dans deux états successifs
(voir le mécanisme d’éclatement de conditions complexes dans le chapitre 5).
La machine séquentielle avec actions donnée dans la figure 11.16 tient
compte de ces remarques.
4.3.2 Obtention de la PO
On part de la PO type décrite dans le paragraphe 2. La figure 11.17 décrit
une PO qui permet toutes les actions apparaissant dans les différents états de
la machine séquentielle avec actions donnée précédemment.
262 Conception de circuits séquentiels...
Op2,Op1
Bus A
TPBusA
BusAdresses
JBusA
IBusA
ResetTR
SetTR SelAdr
T
TRBusA
UAL
I J T E M
TR
M
P
InitI MBusB SelUAL
TPBusB
ChI ChJ ChT
ChTEMP Bus B
ComptesRendus
BusRésultat SelEntrées
SelSorties
BusDonnées
Fig. 11.18 – Sorties pour quelques états de l’automate de la figure 11.16 ; sont précisés
les signaux à 1 et à φ et omis ceux à 0.
4.3.4 Optimisation
On peut simplifier ce circuit en n’utilisant plus de registre pour stocker
le booléen trouvé. En effet on peut sortir directement de la boucle quand le
test du prédicat mem[j-1] > t donne VRAI (voir la modification sur la machine
séquentielle de la figure 11.19).
5. Exercices
E11.1 : La racine carrée
Il s’agit de construire un circuit permettant de calculer la partie entière de la
racine carrée d’un entier naturel x. L’algorithme correspondant est donné au
paragraphe 3.2 du chapitre 10. En suivant la méthode décrite dans ce chapitre
264 Conception de circuits séquentiels...
i ←− 1
Délivrer(Fin)
ResNul
i−M
ResNul
j ←− i
t ←− mem[i]
j
ResNul ResNul
j ←− j − 1
Lexique
U : un entier > 0 ; fin : un booleen
Algorithme
Tantque VRAI :
Acquérir(U)
tantque U > 1 :
si U MODULO 2 = 0 alors U ←− U DIV 2
sinon U ←− 3 * U + 1
Délivrer (U)
Délivrer(fin)
Sorties
SelSorties Op
Bus A ChU
UAL
U
SelUAL
Techniques de
l’algorithmique logicielle
Chapitre 12
Le langage machine et le
langage d’assemblage
1. Le langage machine
1.1 Description générique de la machine
Pour définir la notion de langage machine, et proposer des critères de choix
d’un ensemble d’instructions qui constitue un modèle de calcul universel, il
faut tout d’abord définir précisément la machine.
Nous nous restreignons ici au modèle d’architecture de Von Neu-
mann [BGN63] : une machine comporte une unité de calcul (qui réalise des
opérations de base sur les entiers codés en binaire) et une mémoire qui contient
des opérandes et des codes d’opérations (les instructions). Les instructions sont
exécutées dans l’ordre où elles sont rangées en mémoire.
Nous distinguons par la suite :
– Une mémoire de grande taille, dont les éléments sont désignés par des
numéros qu’on appelle des adresses. C’est la mémoire que nous avons ma-
nipulée en en donnant une abstraction par le tableau MEM au chapitre 4.
Nous parlerons souvent de mémoire principale
– Une mémoire de plus petite taille dont les éléments, appelés registres, sont
désignés par des noms ou par des numéros courts. Sur certaines machines
on distingue des registres données et des registres adresses, avec des instruc-
tions travaillant sur l’une des catégories de registres seulement. Sur d’autres
1. Le langage machine 271
machines, au contraire, les registres sont banalisés. Les machines les plus
anciennes ne comportaient qu’un seul registre, dit accumulateur
Nous détaillons au chapitre 14 l’influence de ce genre de distinction sur ce qu’on
appelle la partie opérative du processeur, c’est-à-dire la structure du circuit qui
connecte l’unité de calcul aux bus et aux divers registres.
La distinction entre ces deux types de mémoire permet de tenir compte, dans le
choix d’un jeu d’instructions, de critères tels que le temps d’accès à la mémoire :
les registres sont accessibles plus rapidement que la grande mémoire. Nous ver-
rons au chapitre 15 que les registres sont en général situés physiquement dans
le processeur, contrairement à la grande mémoire. D’autre part les adresses
des octets dans la grande mémoire sont des entiers assez longs (typiquement
32 bits), qui soit ne peuvent pas apparaı̂tre tels quels dans le codage d’une ins-
truction (Cf. Paragraphe 1.3), soit en ralentissemnt l’exécution ; en revanche,
les adresses ou numéros des registres sont de petits entiers — 5 bits sur le pro-
cesseur sparc1 par exemple — et peuvent donc apparaı̂tre comme désignation
d’opérande dans le codage d’une instruction.
Fig. 12.1 – Modes d’adressage usuels. Noter que, pour les adressages indirects par re-
gistre avec pré (ou post) incrémentation (ou décrémentation), l’effet sur
le registre d’indirection est de la forme : Regn ←− Regn op t, où op est
l’opération + ou l’opération −, et t ne vaut pas nécessairement 1. t dépend
de la taille de l’opération, c’est-à-dire de la taille des opérandes. Par exemple,
si l’instruction travaille sur des mots de 32 bits, t = 4. Pour les adressages
relatifs au compteur programme PC, la constante d donnée dans l’instruction
peut éventuellement être multipliée par une constante N. Voir un exemple
paragraphe 1.4.3.
1. Le langage machine 275
L’opération effectuée est prise parmi toutes les opérations de l’unité de cal-
cul, les transferts entre mémoire et registres, les branchements, les instructions
spécifiques éventuelles (Cf. Paragraphe 1.4 pour une liste détaillée d’instruc-
tions).
Si le jeu d’instructions comporte n instructions, le codage compact de la
nature de l’opération demande b = log2 n bits (l’entier immédiatement
supérieur à log2 n). Il n’y a aucune raison pour que le nombre d’instructions
d’une machine soit exactement une puissance de 2, et il existe donc toujours au
moins une configuration d’un vecteur de b booléens qui ne correspond à aucune
instruction. Cela justifie le cas d’erreur dans l’algorithme d’interprétation du
langage machine du paragraphe 1.6.2 ci-dessous, et constitue l’une des causes
d’interruption logicielle étudiées dans la partie VI.
On suppose que la nature d’une instruction comporte implicitement l’infor-
mation sur le nombre d’opérandes. Même si l’on imagine un langage machine
offrant par exemple une addition binaire et une addition ternaire, on considère
que ce sont deux instructions différentes à compter dans les n instructions,
et à coder globalement. Cela donne un codage plus compact que de séparer
le codage de la nature de l’instruction et le codage d’un entier donnant le
nombre d’opérandes (qui pour une grande majorité des opérations est toujours
le même). De manière générale, le choix de la structure des informations qui
constituent une instruction, c’est-à-dire le choix des champs, a une influence
sur la compacité du codage.
276 Le langage machine et le langage d’assemblage
En langage machine il n’y a pas de typage des données, mais les types sont
implicites dans les opérations offertes. Certaines instructions ne sont utilisées
que si le programmeur interprète la correspondance entre vecteurs de booléens
et entiers selon un certain code (binaire pur, complément à 2, virgule flottante,
décimal codé binaire, pixels...). Il existe des opérations qui ont un sens si on
interprète les vecteurs de bits comme le codage d’entiers (ADD, branchements
sur codes de conditions entières, voir ci-dessous), et il existe des opérations
qui les interprètent comme des vecteurs de booléens (AND). En revanche il n’en
existe pas qui les interprètent comme le code ASCII d’un caractère ; ce serait
le cas s’il existait une instruction spécifique du langage machine capable de
transformer un vecteur de bits représentant le caractère ’a’ en vecteur de bits
représentant le caractère ’A’ ; cette opération est évidemment réalisable sur
tout processeur, mais en passant par le codage des caractères par des entiers
ou des vecteurs de booléens. Les seuls types sont donc les entiers et les vecteurs
de booléens ; l’extension MMX [Int97] et VIS offrent de plus des opérations qui
interprètent les vecteurs de 32 bits comme 4 sous-vecteurs de 8 bits.
278 Le langage machine et le langage d’assemblage
31 3029 25 24 19 18 14 13 12 0
1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 00 0 0 0 1 0 1 0 1 0
Opérations sur les entiers Tous les processeurs offrent les opérations d’ad-
dition et de soustraction d’entiers. La même instruction est utilisable pour les
opérations sur les naturels codés en binaire bur et sur les entiers codés en
complément à deux, grâce aux bonnes propriétés du codage en complément à
deux (Cf. Chapitre 3). La différence d’interprétation du codage des entiers ap-
paraı̂t dans les instructions de branchement conditionnels (paragraphe 1.4.3).
Certains processeurs offrent également la multiplication et la division
entière ; mais ce sont des instructions coûteuses en temps ou en surface du cir-
cuit interprète. Le sparc offre une instruction mulscc effectivement exécutable
en temps égal à celui d’une addition, mais cette instruction ne constitue qu’un
pas de la multiplication 32 bits (la version 9 offre une vraie multiplication).
Pour réaliser la multiplication de deux entiers 32 bits, il faut écrire 32 instruc-
tions mulscc en séquence. Le 68000 offre deux instructions muls et mulu de
multiplication de deux entiers signés ou non de 16 bits, dont le résultat est sur
32 bits ; il offre de même deux instructions divs et divu.
Lorsque la multiplication et la division générale n’existent pas, on trouve
toutefois les instructions de décalage arithmétique, qui permettent la division
et la multiplication par des puissances de 2. (Cf. Chapitre 3, paragraphe 2.2.3).
1. Le langage machine 279
Opérations sur les vecteurs de booléens Les opérations sur les vecteurs
de booléens sont les extensions bit à bit des opérateurs booléens usuels et, ou,
non, nand, etc. Elles n’existent pas nécessairement toutes. Par exemple le sparc
offre AND et ANDN, OR et ORN, XOR et XORN, mais pas de NOT. ANDN (resp. ORN)
calcule la conjonction (resp. la disjonction), bit à bit, du premier opérande
et de la négation bit à bit du second. Les propriétés de l’algèbre de Boole
permettent de fabriquer les opérateurs manquants en termes des opérateurs
disponibles.
Pour utiliser les opérations sur les vecteurs de booléens dans la compila-
tion des opérations booléennes des langages de haut niveau, il faut inventer
un bon codage des booléens du langage de haut niveau (Cf. Chapitre 4, para-
graphe 2.2.1 et chapitre 13, paragraphe 1.2).
Opérations LOAD
Mémoire
a w
registre 32 bits a+1 x
a+2 y
R a+3 z
31 24 23 16 15 87 0
Opérations STORE
1 octet
Fig. 12.3 – Transferts registres vers mémoire et mémoire vers registres du sparc. On
note ext16(b) (resp. ext24(b)) le mot de 16 (resp. 24) bits obtenu en copiant
16 fois (resp. 24 fois) le booléen b.
1. Le langage machine 283
existent en deux exemplaires : une version qui ne touche pas aux indicateurs,
et une version qui les met à jour.
Ces 4 indicateurs sont : Z, qui est vrai si le résultat de l’opération est 0 ; C,
qui est vrai si l’opération arithmétique a produit une retenue (C pour Carry)
et qui, si l’on interprète les opérandes et le résultat comme des entiers naturels
codés en binaire pur, signifie que le résultat n’est pas codable sur le même
nombre de bits que les opérandes ; N, qui est le bit de poids fort du résultat
(si ce résultat est interprété comme le codage en complément à 2 d’un entier
relatif, si N vaut 1 alors le résultat est négatif) ; V, qui n’a de sens que si
l’on interprète les opérandes et le résultat comme des entiers relatifs codés en
complément à 2, et qui est vrai si le résultat n’est pas représentable sur le même
nombre de bits que les opérandes (V pour oVerflow). Reprendre le chapitre 3
pour un exposé détaillé de la signification des divers indicateurs arithmétiques.
(instruction RTS). Lors du premier appel (exécution de JSR SP1) l’adresse sau-
vegardée est 3 ; l’exécution de l’instruction RTS effectue le retour à cette adresse.
L’instruction de branchement avec sauvegarde de l’adresse de départ est
généralement spécifique : il s’agit d’une instruction de branchement qui n’est
pas ordinaire puisqu’il lui faut intégrer une sauvegarde. Or après le saut il est
trop tard pour sauvegarder l’adresse d’où l’on vient ; avant le saut il n’est pas
toujours très simple de la calculer. Selon les machines l’adresse sauvegardée
est l’adresse qui suit le branchement, ou l’adresse du branchement elle-même ;
le retour de sous-programme doit être cohérent avec ce choix.
Les instructions de branchement à des sous-programmes peuvent être ab-
solues ou relatives, et l’adressage peut-être direct ou indirect par registre, avec
ou sans déplacement. Sur le sparc on dispose de deux instructions de bran-
chement avec sauvegarde, qui peuvent donc être utilisées pour coder des sauts
à des sous-programmes : call et jmpl. call est un branchement relatif à
adressage direct, qui sauvegarde sa propre adresse dans un registre, toujours le
même. jmpl est un branchement absolu à adressage indirect par registre avec
déplacement, qui sauvegarde sa propre adresse dans un registre spécifié par
l’instruction.
Nous détaillons au chapitre 13 l’utilisation des instructions de saut à des
sous-programmes, et la structure de pile sous-jacente, pour le codage des ac-
tions et fonctions paramétrées des langages impératifs usuels, comme celui
présenté au chapitre 4.
Fig. 12.4 – Le langage machine décrit par le lexique d’une machine séquentielle avec
actions. On ne décrit ici que l’instruction soustraction avec mise à jour des
indicateurs arithmétiques
1. Le langage machine 291
SoustReg (1,2,3)
fonctionZ
fonctionZbar
SoustReg (1, 2, 3)
fonctionBGU
fonctionBLEU
SoustReg(1,2,1)
SoustReg(2,1,2)
fonctionA fonctionA
fonctionA
Fig. 12.5 – Traduction d’un algorithme en machine séquentielle à actions et lexique res-
treint : la machine peut bien sûr être simplifiée. Initialement, A0 et B0 sont
respectivement dans les registres 1 et 2.
292 Le langage machine et le langage d’assemblage
1.6.2 Interprétation
Pour décrire l’interprétation d’un programme écrit dans notre petit langage
machine, nous supposons ici que le programme est installé en mémoire, à partir
de l’adresse début. Le problème général de l’installation du codage binaire d’un
programme en mémoire, ou chargement, est étudié au chapitre 18.
Chaque instruction est codée sur 1 octet, plus un ou deux octets d’exten-
sion. L’algorithme d’interprétation est donné figure 12.6.
Noter la complexité de certaines actions, par exemple celle nécessaire à
l’interprétation d’une instruction d’incrémentation en mémoire, avec adres-
sage indirect par registre : MEM[Reg[NumReg]] ←− MEM[Reg[NumReg]] + 1.
Nous verrons au chapitre 14 les contraintes qu’impose une telle action sur ce
qu’on appelle la partie opérative du processeur, c’est-à-dire l’unité de calcul,
les registres et les bus. Au niveau algorithmique, il est facile de remplacer cette
action complexe par une action équivalente :
temp ←− MEM[Reg[NumReg]]
temp ←− temp + 1
MEM[Reg[NumReg]] ←− temp
qui fait apparaı̂tre un nouveau registre temp. Cette nouvelle forme permet de
séparer l’accès en écriture de l’accès en lecture à la mémoire.
Noter également le cas d’erreur lorsque le code d’instruction lu ne corres-
pond à aucune instruction valide (il est en effet possible que le codage de
l’ensemble des instructions en vecteurs de n bits ne constitue pas une fonc-
tion surjective, comme nous l’avons signalé au paragraphe 1.3.1). Dans un
programme d’interprétation du langage machine, on peut émettre un message
d’erreur. Lorsque le langage machine est directement interprété par le proces-
seur, cette erreur doit également être détectée, et signalée au programmeur.
Nous y revenons au chapitre 24, paragraphe 1.2.
En toute rigueur, il faudrait prévoir une détection d’erreur lors de la lecture
d’un numéro de registre dans un octet : NumReg ←− MEM[PC], à moins de
supposer que toute configuration d’un vecteur de 8 booléens correspond effecti-
vement à un numéro de registre existant, c’est-à-dire que la machine comporte
256 registres.
1. Le langage machine 293
lexique
Vecteur8 : un tableau sur [0..7] de booléens
Vecteur32 : un tableau sur [0..31] de booléens
Reg : le tableau sur 0..31 de Vecteurs32 { Les 32 registres }
NumReg : le type entier sur 0..7 ; adresse : le type entier sur 0..tmem−1
MEM : un tableau sur [adresse] de Vecteurs8 { La mémoire }
Z : un booléen { Indicateur d’opération arithmétique }
ADD, BT, BZ, INCRR, INCRM : les constantes de type Vecteur8 : 0, 1, 2, 3, 4
Inst : un Vecteur8 ; PC : une adresse
début : une adresse ; taille : un entier > 0
{ MEM[début ... début+taille] contient les instructions }
algorithme
PC ←− début
tantque PC < début + taille
Inst ←− MEM[PC] { premier octet } ; PC ←− PC + 1
{ Lire no registre, nécessaire pour toutes les instructions (1 octet) }
NumReg ←− MEM[PC] ; PC ←− PC + 1
{ lecture autres octets selon l’instruction }
selon Inst :
Inst = ADD :
{ ajout de la valeur immédiate, troisième octet : }
Reg[NumReg] ←− Reg[NumReg] + MEM[PC] ; PC ←− PC + 1
si Reg[NumReg] = 0 alors Z ←− 1 sinon Z ←− 0
Inst = BT : { NumReg est le numéro du registre d’indirection }
PC ←− Reg[NumReg]
Inst = BZ : { NumReg est le numéro du registre d’indirection }
si Z alors { branchement effectif }
PC ←− PC + Reg[NumReg]
sinon { PC ←− PC + 1 déjà effectué : passage en séquence }
Inst = INCRR : { NumReg est le numéro du registre à incrémenter }
Reg[NumReg] ←− Reg[NumReg] + 1
si Reg[NumReg] = 0 alors Z ←− 1 sinon Z ←− 0
Inst = INCRM : { NumReg est le numéro du registre d’indirection,
qui donne l’adresse de l’emplacement mémoire à
incrémenter }
MEM[Reg[NumReg]] ←− MEM[Reg[NumReg]] + 1
si MEM[Reg[NumReg]] = 0 alors Z ←− 1 sinon Z ←− 0
sinon :
{ code d’instruction invalide. Cf. Chapitre 24 }
à l’autre les fichiers source de ses programmes, les compiler avec le nouveau
compilateur, et les exécuter.
Malheureusement les utilisateurs très anciens ont parfois perdu les fichiers
source de leurs programmes, ou bien, ce qui est le cas pour presque tous les
logiciels commerciaux, ils n’avaient acheté le programme que sous sa forme
exécutable. Ils ne disposent donc plus que du fichier objet, c’est-à-dire un pro-
gramme en langage machine de génération n. Le constructeur doit alors garan-
tir la compatibilité ascendante de ses machines, c’est-à-dire faire en sorte que
le fichier objet de génération n soit interprétable sur la machine de génération
n + 1.
Il y a essentiellement deux solutions. Si les deux machines sont conceptuel-
lement proches l’une de l’autre, le jeu d’instructions de la nouvelle machine
est défini comme un sur-ensemble du jeu d’instructions de l’ancienne. Les an-
ciens programmes sont exécutables directement sur la nouvelle machine ; ils
n’utilisent qu’une partie du nouveau jeu d’instructions, et sont donc peut-être
moins efficaces que ce qu’ils auraient pu être en profitant au mieux du nouveau
jeu d’instructions. C’est le cas entre sparc et ultrasparc.
Si les deux machines sont très différentes, le constructeur fournit un
émulateur du langage machine n sur la machine n + 1. Un émulateur est un
programme, écrit dans un langage quelconque, par exemple C, et compilé sur
la nouvelle machine, avec le nouveau compilateur C. Ce programme est un
interprète du langage machine n. Le code objet des anciens programmes n’est
donc plus directement interprété par un processeur, mais par un programme,
lui-même compilé et exécuté sur un autre processeur.
C’est le cas des macintosh : les processeurs 68000 et PowerPC sont très
différents et il n’y a pas de compatibilité ascendante de leurs jeux d’instruc-
tions. Apple fournit donc un émulateur de 68000 parmi les programmes du
logiciel de base fourni avec les machines à PowerPC.
accès mémoire.
De manière générale, la conception des processeurs est de plus en plus
indissociable de la compilation des langages de haut niveau.
Si l’on pousse le raisonnement à l’extrême, le jeu d’instructions d’une ma-
chine peut être entièrement conçu pour l’exécution de programmes écrits dans
un langage donné. On a ainsi construit des machines LISP, des machines
PROLOG, etc. On entend parler également de machines JAVA. Dans ce der-
nier cas, de quoi s’agit-il exactement ? Les concepteurs du langage JAVA en
ont fait la publicité avec des arguments de portabilité et de sécurité : JAVA
est d’abord compilé en une forme intermédiaire (appelée byte code) qui est
ensuite exécutable, par interprétation, sur tout ordinateur qui possède l’in-
terprète adéquat. L’exécution sous forme d’interprétation est censément plus
sûre que l’exécution par le processeur d’un programme en langage machine ;
en effet, l’outil d’interprétation peut effectuer quelques vérifications sur les
opérations qu’il effectue. Si maintenant on propose un processeur dont le lan-
gage machine est exactement le format intermédiaire produit par la première
phase de compilation de Java, on obtient une machine Java.
En général, la question se pose de savoir s’il vaut mieux concevoir une
machine dédiée à un langage particulier, et capable d’assurer une exécution
efficace des programmes écrits dans ce langage, ou bien concevoir un bon com-
pilateur de ce langage pour machine universelle.
2. Le langage d’assemblage
Le langage machine offre déjà tout ce qui est théoriquement nécessaire pour
programmer n’importe quel algorithme. Il lui manque une notation lisible et
manipulable par un être humain. Le langage d’assemblage est introduit à cet
effet et offre donc :
– tout d’abord une notation textuelle aisément lisible du langage ma-
chine, c’est-à-dire : 1) une notation des opérations de la machine (les
mnémoniques) et de leurs opérandes, 2) un ensemble de directives de
réservation et d’initialisation de la mémoire
– la possibilité d’introduire des commentaires
– une notion de zones distinctes dans un programme : la zone des instructions
(TEXT) et la zone de données (DATA, BSS), ainsi qu’une notation qui
permet de repérer facilement les portions de programme appartenant à l’une
ou l’autre de ces deux zones.
– un mécanisme de nommage des positions dans la zone des instructions ou
dans la zone de données, qui permet de s’abstraire des valeurs explicites
d’adresses mémoire.
Notons qu’il peut exister plusieurs langages d’assemblage pour le même lan-
gage machine. Les différences résident en général dans la notation des modes
2. Le langage d’assemblage 297
VAL=250
ISE=-125
.data ! début de zone de données
XX : ! étiquette
.long 0x15 ! 4 octets initialisés avec le vecteur de
! bits décrit par 0x15 en hexadécimal,
! repérables par l’étiquette XX
YY : .half -12 ! 2 octets initialisés avec le codage
! de -12 (nécessairement en complément à 2)
.byte VAL ! un octet initialisé avec le codage
! de 250 (nécessairement en binaire pur)
.byte ISE ! un octet initialisé avec le codage
! de -125 (nécessairement en complément à 2)
.skip 12000 ! une zone contiguë de 12000 octets,
! non initialisés.
.asciz "toto" ! 5 octets, initialisés avec les codes
! ASCII des caractères ’t’, ’o’, ’t’, ’o’
! et un octet mis à zéro pour finir.
.align 4 ! directive d’alignement
ZZ : .long XX ! 4 octets initialisés avec le codage
! binaire de l’adresse absolue
! représentée par l’étiquette XX.
4 premiers sont initialisés avec les codes ASCII des caractères ’t’, ’o, ’t’,
’o’ et le dernier avec un caractère de code 0. On respecte ainsi la convention
de représentation des chaı̂nes de caractères utilisée en C, c’est-à-dire sous la
forme d’une adresse de début, sachant que la chaı̂ne est marquée par 0.
.skip est une directive de réservation mémoire sans initialisation. On la
trouve plutôt en zone BSS que DATA, puisque la zone BSS permet de réserver
de la mémoire non initialisée (Cf. Chapitre 18).
.align est une directive de cadrage mémoire nécessaire pour tenir compte
des contraintes d’alignement mémoire dont nous avons parlé au paragraphe 2.3
du chapitre 4. La directive .align 4 (resp. .align 2) tient compte de toutes
les réservations mémoire effectuées auparavant, et ménage un espace perdu de
la taille nécessaire pour atteindre la prochaine adresse multiple de 4 (resp. 2).
4. Un exemple de programme
Considérons l’algorithme qui permet de déterminer le nombre de bits à 1
dans la représentation en binaire pur d’un entier :
4. Un exemple de programme 303
Lexique :
x : un entier ≥ 0 { la donnée }
NombreDeUns : un entier ≥ 0 { le résultat à calculer }
Algorithme :
NombreDeUns ←− 0
tantque x = 0
si x mod 2 = 0 alors NombreDeUns ←− NombreDeUns + 1
x ←− x div 2
Nous donnons ci-dessous des programmes correspondants dans des lan-
gages d’assemblage possibles pour les processeurs sparc et 68000, et les pro-
grammes en langage machine correspondants. Noter qu’en langage d’assem-
blage les opérations arithmétiques notées x mod 2 et x div 2 sont remplacées
par des manipulations explicites de vecteurs de booléens (décalage logique et
masquage).
L’instruction nop n’existe pas en sparc. Elle est codée par une instruc-
tion ayant un effet nul : une addition ou une disjonction portant sur le
registre spécial %g0.
Exemple 3 : d2040000 ld [%l0], %o1
Cette instruction peut être codée complètement. Le mot de 32 bits se
décompose en :
code op reg d code op reg s 1 i
1 1 0 1 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0
inutilisé reg s 2
0 0 0 0 0 0 0 0 0 0 0 0 0
308 Le langage machine et le langage d’assemblage
5. Exercices
E12.1 : Machines à 0, 1, 2 ou 3 références
Nous allons étudier l’écriture d’un algorithme simple dans un programme en
langage d’assemblage de jeux d’instruction de différents types. Dans la suite de
cet exercice nous notons val une valeur immédiate et adr une adresse. Nous
utilisons les conventions d’écriture du langage d’assemblage similaire au sparc
décrite dans ce chapitre. ope représente le mnémonique d’une instruction
parmi add, mult, sub et div, OP est l’opération arithmétique associée.
Traduire pour chacune des machines et langages d’assemblage associés
décrits ci-après l’algorithme suivant, en convenant que A est à l’adresse a,
. . .et Y à l’adresse y :
Lexique :
A, B, C, D, E, F : des entiers { des données }
Y : un entier { le résultat à calculer }
Algorithme :
Y ←− (A + B + C) / (D * E * F - 3)
Même exercice avec le sparc. Pour chaque type de machine, observer la
taille du code obtenu en nombre d’instructions, estimer le nombre d’octets
nécessaires au codage du programme.
1) Jeu d’instructions à 1 référence et 1 seul registre : la machine correspon-
dant à ce type d’instruction possède un registre de calcul appelé accumulateur
(noté acc) qui est toujours utilisé dans les instructions de calcul. L’accumula-
teur est un des deux opérandes et le résultat est forcément stocké dans celui-ci.
L’ensemble des instructions ne possède qu’une référence :
instruction signification
ope val acc ←− acc OP val
ope [adr] acc ←− acc OP MEM[adr]
store adr MEM[adr] ←− acc
load val acc ←− val
load [adr] acc ←− MEM[adr]
2) Jeu d’instructions à 2 références et mode d’adressage restreint : on ra-
joute à la machine précédente d’autres registres (notés Ri). Chacun d’eux
possède les mêmes fonctionnalités que acc. La destination du calcul est tou-
jours un registre. Une des deux références (servant de source et destination)
est en mode d’adressage registre direct.
5. Exercices 309
instruction signification
ope Ri val Ri ←− Ri OP val
ope Ri [adr] Ri ←− Ri OP MEM[adr]
ope Ri Rj Ri ←− Ri OP Rj
store Ri adr MEM[adr] ←− Ri
load Ri val Ri ←− val
load Ri [adr] Ri ←− MEM[adr]
Les processeurs fabriqués autour de l’année 1975 (famille 8080 d’intel et 6800
de motorola) utilisaient ces deux types d’instructions (1 et 2 références).
Dans le cas de l’utilisation de l’accumulateur l’instruction est plus rapide.
3) Machine à 2 références et modes d’adressage variés : on ajoute un mode
d’adressage indirect par registre. Une des deux références est forcément en
mode d’adressage registre direct (mais pas forcément la destination). Les ins-
tructions load et store sont remplacées par une instruction unique move.
instruction signification
Réf. destinations en mode d’adressage registre direct
ope val, Ri Ri ←− val OP Ri
ope [adr], Ri Ri ←− Ri OP MEM[adr]
ope [Rj], Ri Ri ←− Ri OP MEM[Rj]
Réf. destination en mode d’adressage absolu
ope Ri,[adr] MEM[adr] ←− MEM[adr] ope Ri
Réf. destination en mode d’adressage reg. indirect
ope Ri,[Rj] MEM[Rj] ←− MEM[Rj] OP Ri
Instruction move
move Rj, Ri Ri ←− Rj
move val, Ri Ri ←− val
move [adr], Ri Ri ←− MEM[adr]
move Ri, [adr] MEM[adr] ←− Ri
Les processeurs de la famille 68000 de motorola (à patir de l’année 1980) a
un jeu d’instructions de structure similaire avec un jeu de mode d’adressage
encore plus large.
4) Machine à 0 référence : c’est une machine qui nécessite une pile. Les
instructions d’opération se font toujours avec les deux opérandes qui sont au
sommet de la pile (et qui sont alors enlevées de la pile), le résultat est stocké
sur la pile. Ces instructions n’ont donc pas besoin de références. Il faut pouvoir
stocker des valeurs sur la pile avant les calculs, on ajoute donc deux instructions
particulières push et pop. Celles-ci ont une référence. On suppose ici que le
pointeur de pile SP pointe sur le dernier emplacement occupé et que la pile
progresse en diminuant SP.
310 Le langage machine et le langage d’assemblage
instruction signification
ope MEM[SP+1] ←− MEM[SP] OP MEM[SP+1] ; SP ←− SP+1
push val SP ←− SP - 1 ; MEM[SP] ←− val
push [adr] SP ←− SP - 1 ; MEM[SP] ←− MEM[adr]
pop [adr] MEM[adr] ←− MEM[SP] ; SP ←− SP + 1
ments binaires faisant intervenir des conditions prédéfinies sur les indicateurs
arithmétiques (paragraphe 1.3).
A partir des machines séquentielles à lexique restreint, on obtient facilement
des textes de programmes en langage d’assemblage (paragraphe 1.4).
On parlera par la suite de taille du lexique. Attention, cette taille peut dépendre
de l’ordre de déclaration des variables, qui a une influence sur la position et
la taille des trous nécessaires aux contraintes d’alignement. Noter que, dans le
cas d’un programme à un seul bloc, les variables ont toutes la même durée de
vie, qui est celle du programme lui-même.
Dans l’exemple ci-dessous, la mémoire est un tableau d’octets, et on dispose
des affectations de tailles 1, 2 et 4 :
N : l’entier 42
Entier32s : le type entier dans [−232−1 , 232−1 − 1]
Entier16ns : le type entier dans [0, 216 − 1]
Structure : le type < x : un Entier32s, y : un Entier16ns>.
c1, c2 : des caractères ; a : un Entier32s ; b : un Entier16ns
T : un tableau sur [0..N-1] de Structures
A1 .
:.. ! codage de l’action A1
C1 :
... ! codage de la condition C1
Bicc A6 ! branchement sur non C1
A2 .
:.. ! codage de l’action A2
C2 .
:.. ! codage de la condition C2
Bicc A4 ! branchement sur non C2
A1 A3 :
... ! codage de l’action A3
tantque C1 ba A5 ! branchement inconditionnel
A2 A4 :
... ! codage de l’action A4
si C2 alors A3 sinon A4 A5 :
... ! codage de l’action A5
A5 ba C1 ! branchement inconditionnel
A6 A6 .
:.. ! codage de l’action A6
Fig. 13.1 – Séquentialisation des codages en langage d’assemblage des états d’une ma-
chine séquentielle à actions et lexique restreint.
Fig. 13.2 – Séquentialisation des codages en langage d’assemblage des états d’une ma-
chine séquentielle à actions et lexique restreint : exemple du pgcd, Cf. Cha-
pitre 12, figure 12.5. Le programme en langage d’assemblage est obtenu de
manière systématique ; il peut ensuite être simplifié de plusieurs façons : sup-
pression de la deuxième comparaison SUBcc r1, r2, g0 ; remplacement du
BA finsi par BA boucle directement.
paragraphes 2.3 et 2.4. Enfin le paragraphe 2.5 introduit le lien dynamique des
compilateurs, et le paragraphe 2.6 résume l’occupation de la mémoire lors de
l’exécution d’un ensemble de procédures et fonctions paramétrées.
Dans le cas où une procédure A appelle une procédure B, nous appelons
contexte appelant l’ensemble des variables de A et contexte appelé l’ensemble
des variables de B.
1. CalculFibo (4, B)
1.1 CalculFibo (3, f1)
1.1.1 CalculFibo (2, f1’)
1.1.1.1 CalculFibo (1, f1”)
f1” ←− 1
1.1.1.2 CalculFibo (0, f2”)
f2” ←− 1
1.1.1.3 f1’ ←− f1” + f2”
1.1.2 CalculFibo (1, f2’)
f2’ ←− 1
1.1.3 f1 ←− f1’+ f2’
1.2 CalculFibo (2, f2)
1.2.1 CalculFibo (1, f1’)
f1’ ←− 1
1.2.2 CalculFibo (0, f2’)
f2’ ←− 1
1.2.3 f2 ←− f1’ + f2’
1.3 B ←− f1+f2
CalculSomme :
MEM[MEM[aR]] ←− MEM[aA] + MEM[aB]
algorithme du programme principal
... { traduction de Lire (X) ; Lire (Y) }
MEM[aA] ←− 4 MEM[aX] + 1
MEM[aB] ←− 4 MEM[aY] − 2
MEM[aR] ←− 4 aZ
CalculSomme
... { traduction de Ecrire (Z) }
Passage des données par valeur : Les valeurs des expressions qui consti-
tuent les paramètres données effectifs doivent être calculées puis placées en
mémoire par l’appelant, à un endroit connu de l’appelé qui les lira. Ainsi
on calcule la valeur de X+1, c’est-à-dire MEM[aX] + 1, et on la recopie dans
MEM[aA].
différentes variables du lexique local suit la même démarche que celle présentée
au paragraphe 1.1.2. Une fois obtenue une adresse de base p par allocation
dynamique, on dispose les variables les unes après les autres en ménageant des
espaces éventuels pour tenir compte des contraintes d’alignement.
Cette transformation n’a pas encore permis d’éliminer le lexique local : on
a remplacé les variables d’origine par une seule : p, mais il en faut toujours
autant d’exemplaires que d’appels de CalculFibo.
Toutefois, on peut poursuivre la transformation du programme. En effet, on
remarque que, vue la structure des appels d’actions et fonctions, la dernière
zone allouée est toujours la première libérée (autrement dit, les appels sont
bien parenthésés : on ne peut pas successivement entrer dans A ; entrer dans
B ; sortir de A, sortir de B). On profite donc de cette situation très particulière
pour ne pas utiliser un mécanisme général d’allocation et libération d’espaces
mémoire, dont la complexité provient justement de la gestion des trous qui
apparaissent si l’on ne libère pas toujours le dernier alloué.
On propose de réaliser l’allocation mémoire par les algorithmes de la fi-
gure 13.9. Le corps des actions est très court ; si l’on suppose de plus qu’il
y a toujours de la place, on peut remplacer tout appel Allouer (p, n) par
pp ←− pp − n ; p ←− pp et tout appel Libérer (p, n) par pp ←− pp + n. On
peut ensuite éliminer la variable locale p et ne garder que la variable globale
pp. Cela donne l’algorithme de la figure 13.8. On dit que la mémoire est gérée
en pile, pour rendre compte du fait que les allocations se font selon un ordre
dernier alloué/premier libéré (Cf. Chapitre 4, paragraphe 5.).
MEM
0
pp : un pointeur
Initialiser :
pp ←− tmem
adresses croissantes
Zone T+1
Allouer (p, n) : libre
si pp − n < T+1 alors
p ←− NIL
sinon pp
pp ←− pp − n Zone
p ←− pp occupée
tmem − 1
Libérer (p, n) :
pp ←− pp + n tmem
Fig. 13.9 – Gestion mémoire simplifiée : pour allouer une nouvelle zone de taille n, on
déplace le pointeur pp de n octets vers les adresses décroissantes. La zone
ainsi rendue disponible est entre les adresses pp incluse et pp+n exclue.
Fig. 13.10 – Elimination complète des lexiques et des paramètres dans CalculFibo (On
a supposé taille 2 entiers = 8).
2. Cas des programmes à plusieurs blocs 329
pp
f1
pp
@B f2
@B (f)
pp 4 (n)
A=4 4
@B A=4
@B A=4
B B B
tmem tmem
tmem
T T pp T
en cours d’exécution, c’est-à-dire juste sous les variables locales du bloc. Sur
la figure 13.12-a, les pointeurs de base d’environnement sont figurés par des
flèches en trait gras ; la notation @x est utilisée pour signifier l’adresse de x.
Lorsqu’on passe d’un bloc appelant à un bloc appelé, il suffit de placer
la variable pb à la base de l’environnement du bloc appelé. Lorsque le bloc
appelé se termine, il faut replacer pb à sa position antérieure, c’est-à-dire la
base de l’environnement du bloc appelant. Contrairement à ce qui se passe
pour le pointeur pp, il n’est pas toujours aisé de calculer l’ancienne position
de pb de manière à déplacer pb par une action de la forme pb ←− pb + k. On
introduit donc un mécanisme de sauvegarde de pb, ce qui donne le schéma de
la figure 13.12-b. Chacune des positions de pb pointe sur une case mémoire qui
contient la sauvegarde de l’ancienne valeur (plus bas dans la pile). La suite des
ces sauvegardes successives forme ce que l’on appelle le chaı̂nage dynamique,
ou lien dynamique.
Remarque : Il existe également en compilation une notion de lien statique,
à ne pas confondre avec le lien dynamique. Le lien dynamique chaı̂ne entre eux
les environnements de deux blocs A et B tels que A appelle B, à l’exécution.
Le lien statique chaı̂ne entre eux les environnements de deux blocs A et B tels
que B est défini dans A, dans le texte du programme. Cela peut arriver en
Pascal, Ada, ANSI C, par exemple, mais pas en C classique.
La figure 13.13 donne le programme de calcul de la suite de Fibonacci dans
lequel on a introduit la manipulation de la variable pb.
MEM MEM
... ...
f1 Variables locales ancien pb
f2 de CalculFibo param. @f1
param. @f1 param. 3
param. 3 f1
f1 Variables locales f2
f2 ancien pb
de CalculFibo
param. @B param. @B
param. 4 param. 4
A=4 Variables locales A=4
B B
du prog. principal
(a) (b)
Fig. 13.13 – Elimination complète des lexiques et des paramètres dans CalculFibo et
introduction du pointeur de base d’environnement pb.
2. Cas des programmes à plusieurs blocs 333
et ne préjugent pas de l’implantation de la pile, qui peut fort bien être une
séquence chaı̂née.
Noter aussi que la gestion en pile de la mémoire permet une réutilisation
maximale de la mémoire pour les lexiques locaux de deux blocs qui ne s’ap-
pellent pas l’un l’autre.
adresses sont calculables statiquement, et inscrites une fois pour toutes comme
des constantes dans le programme en langage machine.
x, y, z : des entiers
x ←− 3*(y + 2*z) − 7*(x+y)
z ←− y
pp ←− pp - 4 ; MEM[pp] ←− pb ; pb ←− pp ; pp ←− pp − 8
pp ←− pb ; pb ←− MEM[pp] ; pp ←− pp+4
L’effet de ces instructions est exactement celui décrit plus haut. Noter que
le choix de A6 comme pointeur de base d’environnement est libre, puisque c’est
un paramètre explicite des instructions link et unlink ; c’est une convention
des compilateurs. A7 en revanche est le pointeur de pile obligé.
banc de registres i l o
32 bits
SAVE
RESTORE
i l o
7 registres globaux
appelee:
save
! utilisation des paramètres d’entrée %i0...%i6
! et des variables locales %l0..%l7
restore
jmpl %o7+8, %g0
nop
appelante:
! place les paramètres dans %o0..%o6
call appelee ! sauvegarde de l’adresse de retour dans %o7
nop
...
Pour que la propriété soit toujours vraie il suffit, lors de tout changement de
contexte qui installe une nouvelle fenêtre, d’initialiser le registre %o6 de la
nouvelle fenêtre.
Or l’instruction save du sparc se comporte comme une addition, qui
interprète la désignation de ses opérandes dans la fenêtre de départ, et la
désignation du résultat dans la fenêtre d’arrivée. Une instruction save %o6,
-64, %o6 permet donc d’initialiser le registre %o6 de la nouvelle fenêtre d’après
la valeur du registre %o6 de l’ancienne : l’instruction décale le pointeur vers les
adresses inférieures, réservant ainsi un espace de la pile de taille 4 ∗ 16 = 64
pour 16 registres de 4 octets. L’ancien pointeur de pile, qui s’appelait %o6
dans le contexte de l’appelant, est toujours accessible. Il s’appelle %i6 dans
le contexte de l’appelé. %fp, pour Frame Pointer, est un synonyme de %i6 en
assembleur. Le programme de la figure 13.19 illustre ce mécanisme.
4. Exercices
E13.1 : Observation de l’exécution d’une action récursive
Considérons l’algorithme de calcul de la suite de Fibonacci (Cf. Figure 13.3)
et plus particuilièrement la traduction décrite dans le paragraphe 2.5 et la
figure 13.10. Dessiner les différents états du tableau MEM et plus précisément
la partie pile au cours de l’exécution de l’action calculFibo avec la valeur 4 pour
la variable A.
fenêtre 1
i
save %o6, -64,%o6
l Zone de sauvegarde
de la fenêtre 2
fenêtre 2
PILE
o i
6 0
7
restore
o
fenêtre2.SP
6
7 64
fenêtre1.SP = fenêtre2.FP
Fig. 13.18 – Utilisation des fenêtres de registres avec réservation de place dans la pile
pour la sauvegarde des fenêtres. Noter que dans l’écriture de l’instruction
save %o6, -64, %o6, le premier %o6 désigne celui de l’ancienne fenêtre
et le second celui de la nouvelle fenêtre.
appelee:
save %o6, -64, %o6 ! ou save %sp, -64, %sp
! réserve une zone de 64=16*4 octets dans la pile, pour
! la sauvegarde des registres i et l de ce nouveau contexte.
! ... utilisation des paramètres d’entrée %i0...%i6
! et des variables locales %l0..%l7 ...
! retour et restauration de la fen^etre
jmpl %i7+8, %g0
restore
appelante:
...
call appelee ! sauvegarde de l’adresse de retour dans %o7
nop
appelant PILE
0
i save %o6, -taille,%o6
taille > 64 appelé.sp
sauvegarde
registres
l i, l
de l’appelé
Contexte
appelant.sp =
o i
appelé.fp var. locales
6
7 sauvegarde
registres
l i, l
Contexte de
paramètres
l’appelant
>6
6 var. locales
7 appelant.fp
appelé
comme dans le cas des actions : on passe leur valeur. Le résultat d’une fonc-
tion est calculé par l’appelé sans que l’appelant lui ait fourni l’adresse d’une
de ses variables. Il faut donc choisir un mécanisme qui permet à la fonction
appelée d’ecrire le résultat qu’elle calcule dans un emplacement mémoire connu
du contexte appelant, qui pourra le lire. On peut examiner deux solutions : le
résultat est stocké dans un registre ou dans la pile. Noter que selon le type du
résultat de la fonction (par exemple s’il s’agit d’un produit de types ou d’un
tableau) la solution de la pile est plus facile à mettre en oeuvre.
lexique
i, j, x : des entiers ; N : l’entier ... { donné }
algorithme
x ←− 0
i parcourant 0 .. N
j parcourant 0 .. N
x ←− x + i * j
lexique :
X : l’entier constant ...
algorithme :
tant que X = 1
si X reste 2 = 1 { X est impair }
X ←− 3 × X + 1
sinon { X est pair }
X ←− X quotient 2
4. Exercices 347
A la charnière du logiciel et du
matériel...
Chapitre 14
Le processeur : l’interprète
câblé du langage machine
Processeur Mémoire
SelMem
Partie contrôle
l/e
RI
PC
bus adresses
données
UAL
Partie opérative
des variables et la mémoire comme un tableau. Nous donnons ensuite une des-
cription sous forme de machine séquentielle avec actions aux états de laquelle
on associe des microactions : ce sont les opérations effectivement réalisables
par la partie opérative. L’objectif est de montrer comment on peut concevoir
la partie opérative et la partie contrôle d’un processeur, étant donné le jeu
d’instructions retenu.
mémoire
0000 0010 code de l’opération ld
0011 valeur immédiate 3
clr 1 0011 code de l’opération st
ld #vi 2 vi 1000 adresse 8
st [ad] 3 ad 0100 0101 code de l’opération add
1000 adresse 8
jmp ad 4 ad
0100 code de l’opération jmp
add [ad] 5 ad
0111 0100 adresse 4
(a)
(b)
Fig. 14.2 – (a) Codage des instructions ; (b) représentation en mémoire d’un programme
en langage machine
lexique :
entier4 : le type entiers représentés sur 4 bits
{ les opérations arithmétiques sont donc modulo 16 }
PC, Acc : des entier4
tailleMem : l’entier 16
Mem : un tableau sur [0..tailleMem-1] d’entier4
algorithme d’interprétation des instructions :
PC ←− 0
tant que vrai
selon Mem[PC]
clr : Acc ←− 0 ; PC ←− PC + 1
ld : Acc ←− Mem[PC + 1] ; PC ←− PC + 2
st : Mem[Mem[PC + 1]] ←− Acc ; PC ←− PC + 2
jmp : PC ←− Mem[PC + 1]
add : Acc ←− Acc + Mem[Mem[PC + 1 ]] ; PC ←− PC + 2
chRI2
RI2
0 1
chRI1 SelAccPC
RI1
resetAcc resetPC
ACC chACC PC chPC
ADD/INCR
sAccUAL sPCUAL
sAccDon UAL
Mem BusDon
sRI2Ad sPCAd
Mem BusAd Zu, Nu, Cu, Vu
Fig. 14.5 – Commandes et transferts associés aux microactions. Les notations UAL(1)
et UAL(2) désignent l’entrée 1 et l’entrée 2 de l’UAL.
360 Le processeur : l’interprète câblé du langage machine
E0 PC ⇐= 0
E1
RI1 ⇐= Mem[PC]
RI1 = clr
RI1=clr E2
E3 PC ⇐= PC + 1
ACC ⇐= 0 E4
RI2 ⇐= Mem[PC]
RI1=ld RI1=jmp
E5 E6
ACC ⇐= RI2 PC ⇐= RI2
RI1=st
E7
RI1=add
Mem[RI2] ⇐= ACC
E9 PC ⇐= PC + 1
E1 PC ⇐= 0
E2
RI1 ⇐= Mem[PC] PC ⇐= PC + 1
Etat 8
ri3.ri2.ri1.ri0 chACC
chACCouPC
chPC
ri3.ri2.ri1.ri0
Etat 2 ou Etat 4
Fig. 14.8 – Production des commandes chACC et chPC selon la commande chACCouPC,
le code opération et l’état de l’automate de contrôle. chACCouPC est émise
par la partie contrôle, chACC et chPC sont reçues par la partie opérative.
chACC est activée lorsque l’automate de contrôle est dans l’état 8 ou lorsque
chACCouPC est vraie alors que l’instruction courante est ld (codée par 2 en
décimal). chPC est activée lorsque l’automate de contrôle est dans un des
états 2 ou 4 ou lorsque chACCouPC est vraie alors que l’instruction courante
est jmp (codée 4 en décimal).
5. Extensions du processeur
Dans ce paragraphe, nous étudions des extensions de deux types pour le
processeur : augmentation du nombre de registres utilisables par le program-
meur et extension du jeu d’instructions.
ld st ou add clr
vi ad numéro de registre
numéro de registre numéro de registre
BusA
sACC0A
ACC15
ACC0
ACC1
UAL
Mem sACC0Don BusDon
BusB
sRI2B
R R
I I
1 3
R 1 P
I C CALC
0
2
microaction commandes
ACCRI3 ⇐= 0 resetACC
ACCRI3 ⇐= RI2 sRI2B, chACC
Mem[RI2] ⇐= ACCRI3 sACCDon, sRI2Ad, SelMem, Ecr
ACCRI3 ⇐= ACCRI3 + Mem[RI2] sACCA, sRI2Ad, SelMem, Lec, chACC
RI3 RI3
resetACC0 chACC0
resetACC resetACC1 chACC chACC1
resetACC15 chACC15
RI3 RI3
sACC0Don sACC0A
sACCDon sACC1Don sACCA sACC1A
sACC15Don sACC15A
numéro est la valeur contenue dans RI3 est remis à zéro. De même, les 16 états
correspondant au chargement des 16 registres peuvent être remplacés par un
état avec la microaction : ACCRI3 ⇐= RI2. Pour l’instruction st, on va définir
la microaction : Mem[RI2] ⇐= ACCRI3 et pour l’instruction add la microac-
tion : ACCRI3 ⇐= ACCRI3 + Mem[RI2]. La table de la figure 14.11 donne les
commandes associées à chacune de ces nouvelles microactions.
Il faut ajouter le matériel nécessaire pour élaborer les commandes
resetACC0, . . ., resetACC15, chACC0, . . ., chACC15, sACCDon0, . . .sACCDon15,
sACC0A, . . ., sACC15A à partir des commandes resetACC, chACC, sACCDon,
sACCA et du contenu de RI3. La figure 14.12 décrit ces circuits réalisés avec
un décodeur.
La description de la partie contrôle paramétrée est ainsi la même que pour
un seul registre ACC.
RI1 = clr
RI2 ⇐= Mem[PC] PC ⇐= PC + 1
UAL
RI1=brn RI1=brn
et N et N Nu
Registre Etat
PC ⇐= RI2
N
* * vers partie controle
Fig. 14.13 – Extrait de la partie contrôle pour le traitement de l’instruction brn. Les
flèches notées * ont pour cible l’acquisition de l’instruction suivante, c’est-
à-dire l’état E2 de la figure 14.7.
6. Exercices
E14.1 : Branchement conditionnel général
On veut ajouter au langage machine du processeur étudié dans ce chapitre une
instruction BRcc où cc représente l’un des codes de conditions classiques de
tout processeur. Etudier les conséquences de cette modification sur la partie
opérative et la partie contrôle du processeur. On peut considérer un codage
des différentes conditions cc sur 4 bits b1, b2, b3, b4 (ou prendre le codage
d’un processeur existant). Réaliser, en particulier, le circuit combinatoire qui
reçoit en entrée les bits b1, b2, b3, b4 du code opération et les 4 bits Z, N,
C, V et délivre un bit Br disant si la condition donne effectivement lieu à
un branchement ou non. Le bit Br est exploité par la partie contrôle pour
368 Le processeur : l’interprète câblé du langage machine
donnée dans la figure 14.6 selon la technique étudiée au chapitre 10. L’automate
a 10 états que l’on peut coder sur 4 bits e3 , e2 , e1 , e0 ; l’état Ei est représenté
par le codage binaire de l’entier i.
La table de la figure 14.15 donne une partie de la fonction de transition.
Terminer cette table et réaliser la synthèse de cette fonction combinatoire avec
des portes, ou un PLA (Cf. Chapitre 8). Remarquer au passage que l’auto-
mate de contrôle révèle une sous-spécification : rien n’est prévu si le code de
l’instruction dans RI1 n’est pas un code valide. En fait, dans le cas de code
invalide, lors du décodage de l’instruction une interruption est générée par le
processeur (Cf. Chapitre 22).
La partie contrôle a 16 fils de sorties : SelMem, l/e, resetPC, resetAcc, chRI1,
chRI2, chAcc, chPC, add/incr, SelAccPC, sAccUAL, sPCUAL, sAccDon, sRI2Ad,
sPCAd (Cf. Paragraphe 3.2). La partie contrôle étant décrite par un automate
de Moore, les sorties dépendent uniquement de l’état. Nous donnons dans la
figure 14.16 une partie de la fonction de sortie de l’automate. Compléter cette
370 Le processeur : l’interprète câblé du langage machine
30 25 23 18 14 12 0
0 code op C I S2
(a)
Rd ou Rs valeur immédiate (13bits)
S1 ou Rx ou no registre (bits 4-0)
30 25 23 18 14 4 0
(c) 0 code op cond Rx S2
0
Fig. 14.18 – Codage des instructions d’un processeur inspiré du sparc : a) ins-
tructions ADD, ADDcc, SUB, SUBcc, SUBRm SUBRcc, AND, OR, XOR,
LOAD, STORE ; b) instructions LDHI et JMPR ; c) instruction JMP.
BusAd
Mem
PC
INCR
ZNCV
COND
T
Bus2
DEC
19
BusRes
Bus1
vers la partie contrôle
31
R
32
ext-sign
1
R R0
13
R
I
BusDon
Mem
Mem BusAd
Bus Source
ZNCV
INCR
D Di T PC
A
Bus Résultat
BusDon Mem
RI
Processeur/mémoire et entrées/sorties
Le chapitre 15 montre comment connecter le processeur et de la mémoire
centrale (ou principale). Il s’agit de mémoire à semiconducteurs, à accès
aléatoire. Cette mémoire est organisée en plusieurs composants dont cer-
tains sont des mémoires mortes programmables (EEPROM) ; d’autres sont
nécessairement des mémoires vives. Cette mémoire sert à l’utilisateur : le pro-
gramme en cours d’exécution et les données associées sont stockés en mémoire
vive. Les informations et le code nécessaire au démarrage de l’ordinateur sont
stockés en mémoire morte.
Pour réaliser des programmes dont la valeur ne se réduit pas à une
constante, l’ensemble processeur/mémoire doit être ouvert vers l’extérieur.
Nous verrons dans le chapitre 16 que la communication avec le monde extérieur
comporte des aspects de câblage (connexion physique de périphériques
d’entrées/sorties) mais qu’il faut surtout gérer des problèmes de synchroni-
sation (Cf. Chapitre 6). Nous reprendrons la description des entrées/sorties
dans la partie VI et verrons des améliorations utilisant le mécanisme des in-
terruptions.
Les périphériques d’entrées/sorties sont très divers. On peut distinguer deux
378 Un système matériel et logiciel simple
1. Le bus mémoire
Nous avons vu au chapitre 9 que le bus mémoire est constitué du bus
de données et du bus d’adresse. Le bus de données est un ensemble de fils
Dn−1 à D0 via lesquels transitent les valeurs échangées par le processeur et
382 Relations entre un processeur et de la mémoire
la mémoire. Ce bus est bidirectionnel (transfert dans les deux sens). Le bus
d’adresse (unidirectionnel) est un ensemble de fils Am−1 à A0 en sortie du
processeur via lesquels ce dernier indique à la mémoire le numéro (l’adresse)
du mot auquel il accède.
Dans l’hypothèse où le temps de cycle de la mémoire est inférieur ou égal
au cycle d’horloge du processeur, les seuls signaux AccèsMem et l/e suffisent
pour gérer le protocole de communication. Nous nous limitons à cette situation
dans ce chapitre pour nous concentrer sur les aspects connexion, organisation
en différents boı̂tiers et accès à des sous-ensembles du mot mémoire.
Notons toutefois que cette hypothèse est assez rarement vérifiée. Le temps
d’accès peut dépendre de la zone mémoire à laquelle on accède (ROM, RAM ou
entrées/sorties). Les processeurs gèrent donc un protocole complet (inspiré du
protocole poignée de mains décrit dans le chapitre 6) et la durée d’un accès peut
être étendue d’une ou plusieurs périodes d’horloge via un signal d’acquittement
piloté par la mémoire. Le nom du signal varie avec les familles de processeurs
(Data Transfert Ack pour le 68000, Memory Hold pour le sparc, Wait ou
ready pour d’autres processeurs 8 ou 16 bits ...).
Di Donnée(no i)
l/e l/e
Aj Adresse (no j)
PROCESSEUR MEMOIRE
DéconnexionProcesseur
l/e l/e
AutD
D Q Adresse (no i)
En
AutAd
AccesMem Sélection boı̂tier
PROCESSEUR MEMOIRE
DéconnexionProcesseur
AutAd
AutD
l/e
T1 T2
Fig. 15.3 – Chronogrammes décrivant l’accès à la mémoire dans le cas d’un bus multi-
plexé : l’intervalle T1 correspond à une lecture mémoire et l’intervalle T2 à
une écriture mémoire.
1
k−
1
A −k
−
−
0
m
m
0
boı̂tier 0
A
A
A
a = b × 2m−k + l b l
boı̂tier 1 2m−k m−k
k B
Sélection
l boı̂tier b du boı̂tier
n
Décodeur
externe Données
boı̂tier 2k − 1 AccèsMem
2m − 1 (a) (b)
Fig. 15.4 – (a) Mémoire de 2m mots organisée avec 2k boı̂tiers de 2m−k mots chacun.
(b) Décodage d’adresse en cascade. B est l’un des 2k boı̂tiers de taille 2m−k .
bus adresses
O3 O2 O1 O0
22
bus données 32
Si l’on veut accéder à des données de taille supérieure à celle du bus données,
il faut réaliser plusieurs accès mémoire, le programmeur organisant lui-même
le stockage de ces données en mémoire.
taille
AccesMem
PROCESSEUR A1 DECODEUR D’ADRESSES
A0
A21 ... A2
SelO3 SelO2 SelO1 SelO0 erreur
l/e
Fig. 15.6 – Décodage d’adresse dans le cas d’accès à des sous-ensembles du mot mémoire
interne de chaque boı̂tier reçoit l’adresse sur 20 bits de l’octet auquel le pro-
cesseur accède.
La figure 15.6 décrit cette organisation. Le tableau 15.7 donne la table de
vérité de la fonction de décodage d’adresses. Notons la présence d’un signal
erreur émis par le décodeur ; ce signal correspond à une demande d’accès à une
adresse invalide, il pourra être associé à une interruption (Cf. Chapitre 22).
Les exercices E15.4 et E15.5 poursuivent cette étude de cas.
Nous avons dit que la taille de la mémoire peut varier dans la vie d’un
ordinateur, les constructeurs prévoyant en effet la possibilité de rajouter des
boı̂tiers mémoire. Le décodeur d’adresses doit avoir été prévu pour ce faire et
c’est lors de l’initialisation de l’ordinateur qu’un programme détecte la quantité
de mémoire réellement présente.
mémoire notée 2X stocke les octets d’adresses paires (adresse = Am−1 , ..., A1 , 0)
et la mémoire notée 2X + 1 stocke les octets d’adresses impaires (adresse =
Am−1 , ..., A1 , 1).
La mémoire “2X” est connectée à l’octet de poids fort du bus données
(D15 , ..., D8 ) et la mémoire “2X + 1” est connectée à l’octet de poids faible du
bus données (D7 , ..., D0 ).
Le processeur indique au dispositif de décodage d’adresses la taille de l’in-
formation à laquelle il accède (octet ou mot de 16 bits) et le bit de poids faible
d’adresse (A0 ) indique s’il s’agit d’une adresse paire ou impaire.
Le décodeur d’adresses produit les signaux de sélection des boı̂tiers
mémoire : Sel2X et Sel2X+1.
Lorsqu’un accès à un mot de 16 bits avec une adresse paire est demandé,
il n’y a aucun problème : un octet de chacun des boı̂tiers étant envoyé (ou
récupéré) sur le bus données.
Lorsque le processeur veut écrire un octet en mémoire le problème est
simple. Le programmeur sait à quelle adresse il écrit et est responsable de
l’organisation de ses données en mémoire. Il lui suffit donc de préciser la taille
de la donnée à écrire. En général des instructions sont prévues pour cela dans
les processeurs ; par exemple, dans le sparc (Cf. Chapitre 12, figure 12.3),
l’écriture de 32 bits dans la mémoire est réalisée par l’instruction ST et l’écriture
d’un octet par une instruction différente (STB, store byte).
En revanche, pour transférer un octet de mémoire vers un registre R (lecture
d’un octet en mémoire), il faut savoir quelle partie du registre est affectée et
que vaut le reste du registre. Le problème est ainsi de sélectionner la bonne
partie du bus données.
Pour traiter ce problème, un circuit C est ajouté au processeur : la fi-
gure 15.9 montre sa position et la table 15.10 décrit la fonction qu’il réalise.
Les octets étant cadrés dans les poids faibles du registre R, si un octet de
394 Relations entre un processeur et de la mémoire
Sel2X Sel2X+1
0 0
2X 2X+1
Am−1 ... A1
2m−1 − 1 2m−1 − 1
D15...8 D7...0
l/e
MEMOIRE
2X 2X+1
PROCESSEUR
C Registre instruction
un registre
Fig. 15.9 – Recadrage des données lors de lecture de sous-multiples du mot mémoire
Type Accès Adresse Type Instruction R15 ... R8 R7 ... R0
16 paire - D15 ... D8 D7 ... D0
16 impaire - ———- ———
8 paire non signé 0 ... 0 D15 ... D8
8 impaire non signé 0 ... 0 D7 ... D0
8 paire signé D15 ... D15 D15 ... D8
8 impaire signé D7 ... D7 D7 ... D0
Fig. 15.10 – Cadrage des données dans un registre lors d’une opération de lecture
mémoire
4. Exercices 395
4. Exercices
E15.1 Faire le schéma détaillé en portes du circuit C de la figure 15.9 en
prenant les codages de taille dans la documentation d’un vrai processeur.
E15.2 Comment peut être étendu le mécanisme décrit au paragraphe 3.2
pour traiter des données de 32, 16 et 8 bits ?
E15.3 Chercher, dans des documentations techniques de processeurs, s’ils
ont ou non une contrainte d’alignement des mots de 2n octets sur frontière
multiple de 2n . En profiter pour regarder quelle convention a été adoptée :
gros-boutiste ou petit-boutiste (Cf. Chapitre 4, paragraphe 2.2.4).
E15.4 : Une mémoire simple (Cet exercice fait suite à l’étude de cas du
paragraphe 3.1)
Le processeur a toujours un bus données de 32 bits mais un bus d’adresses
de 24 bits. Nous disposons de 16 boı̂tiers de 1Mo. Décrire l’organisation de la
mémoire et le décodage d’adresses afin de disposer d’une mémoire de 4 ∗ 220
mots de 32 bits, sachant que l’on veut pouvoir accéder à des octets, à des mots
de 16 bits ou à des mots de 32 bits.
0
ROMs
1M 3 octets en M+3, M+11,
2M M+15
3M 3 mots de 16 bits en
RAMs 2M+2, 2M+10, 2M+14
4M
8M
RAMu
12M
RAMu
16M
Circuits d’entrées/sorties
1. Notion d’entrées/sorties
Pour fixer les idées, considérons une configuration simple avec deux or-
ganes périphériques : un clavier pour les entrées et un afficheur sept segments
(Cf. Exemple E8.2) pour les sorties (Cf. Figure 16.1).
398 Circuits d’entrées/sorties
PROCESSEUR/MEMOIRE
transcodeur transcodeur
.....
ENTREE SORTIE
l/e
Décodage sélection de différents
AccèsMem
d’adresses boı̂tiers mémoire
Sélection Coupleur
Coupleur
Périphérique
Processeur bus adresses
bus données
fils spécialisés
emplacements mémoire
dits registres du coupleur
amplificateur
3 états
1 bit de Di
bus données
D Q
En
AccèsMem
l/e
Fig. 16.3 – Echange entre le processeur et la mémoire sur un bit : lorsque le signal
AccèsMem vaut 1 et le signal l/e vaut 0 la bascule mémorise la valeur présente
sur le bus et lorsque AccèsMem et l/e valent tous deux 1 la valeur mémorisée
dans la bascule est présente sur le bus.
+5V
interrupteur
Di
D Q
En
Ecr Lec
SelCoupleur
lampe
l/e
Fig. 16.4 – Echange entre le processeur et l’extérieur sur un bit : le signal électrique
commandé par l’interrupteur apparaı̂t sur le fil Di du bus données lors d’un
cycle de lecture (SelCoupleur=1 et l/e=1). Si l’interrupteur est fermé le
processeur lira un 0, s’il est ouvert il lira un 1. Par ailleurs la valeur émise
sur le fil Di par le processeur lors d’une écriture (SelCoupleur=1 et l/e=0)
est mémorisée par le verrou ; si cette valeur est 1 alors la lampe s’allume, si
c’est 0 la lampe s’éteint.
404 Circuits d’entrées/sorties
D Q
MONDE EXTERIEUR
... En
... Di
PROCESSEUR
Bus données
D Q
En
A0 EcrS0 EcrS1
LecE0 LecE1
Décodeur du coupleur
Décodeur SelCoupleur
Bus adresses d’adresses sélection de différents
... boı̂tiers mémoire
AccèsMem
l/e
Fig. 16.5 – Coupleur à deux ports d’entrées et deux ports de sortie : le décodeur
d’adresses active le signal SelCoupleur lorsqu’il reconnaı̂t l’adresse qui lui
est fournie comme une adresse du circuit d’entrées/sorties. En supposant
que le bit d’adresse A0 permet de faire la distinction entre les deux mots
du coupleur, le décodeur du coupleur fabrique les signaux de commande des
2 ports de sortie EcrS0 et EcrS1 et des 2 ports d’entrée LecE0 et LecE1, à
partie des signaux SelCoupleur, l/e et A0.
– un booléen processeur prêt qui signifie qu’un nouveau caractère est dispo-
nible et doit être imprimé,
– un booléen imprimante prête, qui signifie que l’imprimante est prête à traiter
un caractère. Après une impression il signifie que le caractère précédent a
été imprimé.
Le coupleur est composé d’un registre de données RegD et d’un registre de
commande RegC auxquels on accède en écriture, et d’un registre d’état RegE
auquel on accède en lecture. Vus du processeur RegD est à l’adresse RD et
contient la donnée, RegC est à l’adresse RC et contient un seul bit significatif :
processeur prêt ; RegE est à l’adresse RE et contient un seul bit significatif
imprimante prête.
Posons comme convention que le signal processeur prêt est actif lorque le
registre RegC vaut 1 et que le signal imprimante prête est actif lorsque le signal
RegE vaut 1.
La figure 16.6 décrit cette organisation matérielle et la figure 16.7 donne les
programmes d’initialisation du coupleur et d’impression d’un caractère stocké
à l’adresse car. Ils sont écrits en langage d’assemblage 68000 (Cf. Chapitre 12
pour un exemple de syntaxe de ce langage d’assemblage). L’exécution de ces
4. Programmation d’une sortie 405
Emetteur
IMP-PRETE = 1
PROC-PRET = 1
NON-PROC-PRET = 0
.data
car : .ascii ’A’ ! code du caractère A
.text
Init : moveq NON-PROC-PRET, RC !le processeur n’a rien à émettre
bus données
Processeur
et D7
décodage D1 RegD
D0 Imprimante
ECRS0 écriture donnée
processeur
RegC
prêt
ECRS1 écriture commande
RegE
imprimante
prête
erreur papier
lecture état
LECE1
IMP-PRETE = 1 ; PROC-PRET = 1
NON-PROC-PRET = 0
ERREUR = 2 ! bit 1 du registre RE : 21
.data
car : .ascii ’A
.text
ImpCar : ! A la fin du traitement D1 contient 0 si tout
! s’est bien passé et 1 s’il y a eu une erreur
att-pret : move.b RE, D1
andi.b ERREUR, D1
bne pb-papier ! plus de papier
move.b RE, D1
andi.b IMP-PRETE, D1
beq att-pret
move.b car, RD
moveq PROC-PRET, RC
att-traite :move.b RE, D1
andi.b ERREUR, D1
bne pb-papier
move.b RE, D1
andi.b IMP-PRETE, D1
bne att-traite
OK : moveq 0, D1
bra fin
pb-papier : moveq 1, D1
fin : moveq NON-PROC-PRET, RC
rts
RegE transfert
possible
LECE1 imprimante prête
lecture état
caractère ait été récupéré par l’imprimante, ce qui est détectable par le fait que
processeur prêt soit inactif. Si cette nouvelle double condition (transfert possible
sur la figure 16.10) est testée à la place de la simple condition imprimante prête,
la boucle d’attente qui suit l’émission d’un caractère peut être supprimée.
Le processeur n’a plus alors qu’à tester transfert possible et écrire le ca-
ractère à transférer dans le registre de données. Le signal processeur prêt est
automatiquement mis à jour par la bascule. La présence de la bascule RS
remplace les accès au registre de commande.
La figure 16.10 décrit le matériel nécessaire à cette nouvelle interface et
le programme d’impression d’un caractère sur l’imprimante dans ce nouveau
contexte est décrit dans la figure 16.11 .
TRANSFERT-POSSIBLE = 1
.data
car : .ascii ’A
.text
ImpCar :
att-pret :move.b RE, D1
andi.b TRANSFERT-POSSIBLE, D1
beq att-pret
move.b car, RD
! la bascule RS passe a 1, l’imprimante sait qu’elle
! doit prendre un caractere
rts
est prêt à traiter un caractère. Après une lecture antérieure cela signifie que le
caractère précédemment envoyé a été récupéré.
Les figures 16.12 et 16.13 décrivent respectivement l’organisation matérielle
et les programmes d’initialisation du coupleur et de lecture d’un caractère.
Récepteur
Fig. 16.12 – Exemple de coupleur de clavier. L’émetteur est le clavier, le récepteur est
constitué du processeur, du coupleur et des décodeurs. Cette organisation
nécessite 2 ports d’entrées (E0 pour la donnée, E1 pour l’état) et un port
de sortie pour la commande (S1).
CLAVIER-PRET = 1
PROC-PRET = 1
NON-PROC-PRET = 0
.data
! un octet initialisé à 0 pour stocker le caractère lu
car : .byte 0
.text
Init : ! le processeur est pr^et à recevoir
moveq PROC-PRET, RC
LireCar :
att-clavier m
:ove.b RE, D1 ! attendre périphérique pr^et
andi.b CLAVIER-PRET, D1
beq att-clavier ! le clavier n’a rien envoyé
! le périphérique est pr^
et : récupérer le caractère
move.b RD, car
moveq NON-PROC-PRET, RC
att-traite :move.b RE, D1 ! attendre le traitement
andi.b CLAVIER-PRET, D1
bne att-traite
! le caractere a été lu
moveq PROC-PRET, RC
rts
Processeur
bus données
et RegD
décodage
lecture donnée
ECRS0 Clavier
R processeur prêt
S
clavier prêt
transfert
RegE
possible
LECE1
lecture état
TRANSFERT-POSSIBLE = 1
.data
car : .byte 0
.text
LireCar :
att-clavier m
:ove.b RE, D1
andi.b TRANSFERT-POSSIBLE, D1
beq att-clavier
move.b RD, car
rts
CDMA COUPLEUR
transfert possible
requête transfert
autorisation requête dma
acq transfert ECRS0
adresse
taille
écriture donnée
i
RegD
demande bus
sélection
coupleur
libère bus
sélection
CDMA
Décodeur d’adresse
Adresses
sélection
mémoire
PROCESSEUR MEMOIRE
Données
Fig. 16.16 – Accès à la mémoire avec DMA : on observe les 5 composants : processeur,
contrôleur de DMA, coupleur, mémoire et dispositif de décodage d’adresse
qui sélectionne les boı̂tiers. Le CDMA comporte des registres décrivant la
zone de mémoire à transférer (adresse et taille) et un registre index (i). Via
deux amplificateurs 3 états, le bus d’adresses est piloté soit par le processeur,
soit par le CDMA. Le bus d’adresses est une entrée pour le CDMA, le
coupleur et la mémoire : il sert à sélectionner le registre du CDMA ou du
coupleur ou le mot mémoire lors d’un accès initié par le processeur. Le bus
d’adresses est une sortie pour les deux maı̂tres : le processeur et le CDMA ;
il sert alors à sélectionner le registre du coupleur ou le mot mémoire.
414 Circuits d’entrées/sorties
7. Exercices
E16.1 : Circuit d’entrées/sorties
Récupérer la documentation d’un circuit d’entrées/sorties (par exemple RS232,
PIA). Repérer les registres du coupleur. Retrouver l’implantation des signaux
évoqués dans ce chapitre ; en général, ils sont représentés par certains bits des
registres du coupleur. Etudier les types de problèmes gérés par le circuit. Ecrire
les programmes d’entrées/sorties.
Pilotes de périphériques
ToucheAppuyée : −→ un entier
{ Retourne un entier < 0 si aucune touche n’est enfoncée ; retourne un
entier ≥ 0 dans le cas contraire, et c’est le code émis par la lecture du
coupleur de clavier }
c, t : des caractères
c ←− 1 Mem [ADRCLAVIER]
t affect c ET BITSTOUCHE { ET bit à bit pour masquage }
si c ET TOUCHEPRESENTE = 0
{ aucune touche appuyée }
t ←− -1
ToucheAppuyée : t
alors un petit circuit séquentiel qui balaie les lignes à tour de rôle et mémorise
la première touche appuyée rencontrée.
Dans la suite, pour fixer les idées, nous considérons que la lecture du cou-
pleur de clavier retourne la position de la touche sur les bits 0 à 5 du bus de
données, l’état de la touche majuscule sur le bit 6 et la présence d’une touche,
en bit 7.
Nous définissons ainsi la fonction ToucheAppuyée qui donne le numéro de
touche appuyée par l’utilisateur (Figure 17.1). Attendre l’enfoncement d’une
touche t s’écrira :
répéter t ←− ToucheAppuyée() jusqu’à t ≥ 0
De même, attendre le relâchement de la touche t s’écrira :
répéter tt ←− ToucheAppuyée() jusqu’à tt = t
en code ASCII en fonction de la topologie du clavier, que l’on peut par exemple
décrire par un tableau (indicé par le numéro global de touche) :
{ Données du pilote }
ModeLigne : un booléen
ModeEcho : un booléen
MaxLigne : un entier > 0
Ligne : un tableau sur 0..MaxLigne − 1 de caractères { le tampon }
tailleligne : un entier ≥ 0
DebLigne : un entier sur 0..MaxLigne − 1 { pointeur }
maximale autorisée par le support n’est atteinte que pour la piste intérieure
(la plus courte).
La fréquence de transfert pourrait être adaptée à la longueur des pistes pour
exploiter au mieux le support, les pistes les plus externes ayant plus de secteurs.
Le prix à payer est une électronique de lecture/écriture plus sophistiquée et une
légère complication des algorithmes de localisation des données sur le disque,
le numéro de piste ne pouvant plus être obtenu par simple division.
temps d’accès piste du disque. La variable piste courante est mise à jour. La tête
est ensuite sélectionnée en lecture pour consulter les en-têtes d’enregistrement.
Le numéro de piste éventuellement contenu dans la première en-tête passant
sous la tête après le déplacement est comparé avec la valeur de la variable piste
courante. Un désaccord indique une erreur de calibrage du compteur de piste.
Le remède consiste à ramener la tête sur la piste 0, à remettre à 0 la variable
piste courante et à recommencer le processus depuis le début.
L’étape suivante consiste à attendre le passage des en-têtes qui défilent sous
la tête et d’en comparer le numéro de secteur avec celui du secteur recherché.
Si cette information est absente de l’en-tête, il suffit d’attendre le passage du
début de piste et de compter les en-têtes à partir de celui-ci.
La détection de la bonne en-tête précède immédiatement le passage du
bloc de données du secteur sous la tête. La tête est commutée en écriture si
nécessaire, et le transfert commence. Les données sont transférées bit par bit
à la cadence imposée par la rotation du disque.
La sérialisation de chaque octet est effectuée par un registre à décalage du
contrôleur de disque, le processeur se contentant de déposer ou de récupérer
l’octet dans le registre de données du contrôleur.
La valeur de CRC est calculée pendant le transfert, une erreur pouvant
être éventuellement détectée. L’écriture d’un secteur peut être suivie d’une
relecture de vérification au tour de piste suivant.
En lecture, l’erreur éventuelle peut être due à une petite erreur de posi-
tionnement de la tête ou dans le cas d’une disquette à une poussière sur le
média. L’erreur peut être transitoire et corrigée en déplaçant la tête puis en la
ramenant à nouveau sur la piste pour une nouvelle tentative d’accès. Au-delà
d’une dizaine de tentatives infructueuses, l’erreur peut être considérée comme
fatale, et la donnée irrécupérable.
Si un nouveau cycle écriture-lecture donne à nouveau une erreur, le secteur
(voire toute la piste) est probablement défaillant et devra être marqué comme
tel et retiré de la liste de secteurs utilisables.
Le cadencement des accès aux secteurs est défini par la rotation du disque et
la fréquence de transfert des octets s’impose à l’ensemble processeur/mémoire.
Si la cadence de transfert n’est pas scrupuleusement respectée, un ou plusieurs
octets seront perdus et le transfert se terminera par une erreur.
L’exécution d’une boucle de transfert par le processeur peut s’avérer trop
lente pour le débit du disque. Par exemple, une fréquence de transfert approxi-
mative de 160 Mbits/s représente 20 Mo/s, soit 50 ns par octet ; c’est à peu
près le temps de cycle d’une mémoire centrale.
Pour augmenter le débit, on transfère les données par mots de 32 ou 64 bits
plutôt qu’octet par octet moyennant les contraintes d’alignement d’adresses des
tampons, à exploiter les accès en mode rafale (Cf. Chapitre 9), et à confier la
boucle de transfert à une unité d’accès direct à la mémoire (Cf. Chapitre 16).
432 Pilotes de périphériques
D’autre part les fonctions de contrôle du pilote pourraient être les sui-
vantes : accès aux paramètres : taille d’un secteur, du disque, nombre de
secteurs par bloc ; formatage physique du disque ; etc. Certaines fonctions
sont spécifiques des unités à support amovible : marche/arrêt rotation, charge-
ment/déchargement des têtes, détection de protection contre l’écriture, éjection
du média (disquette, CDROM).
0x00
0x3E
0x20
0x20
0x3C
0x20
0x20
0x3E
0x00
Ecran
Périphériques
cathodique
caractère
Bus Mémoire
DMA de Accélérateur
graphique Processeur
Rafraı̂chissement
Pour un affichage plus performant, les primitives grahiques les plus cou-
rantes (dont le tracé de segments de droites et de figures géométriques) peuvent
être câblées (Cf. Chapitre 11) et déléguées à un circuit accélérateur. Le bus peut
également être scindé en deux parties (césure en pointillé) par des connexions
de type trois états, pour autoriser des accès simultanés de l’accélérateur gra-
phique à la mémoire d’écran et du processeur à la mémoire principale ou aux
autres entrées/sorties.
Le processeur principal qui génère l’information à afficher peut donc adres-
ser de la même manière la mémoire d’écran et la mémoire principale.
A l’opposé, l’ensemble de la figure 17.6 peut constituer un terminal gra-
phique intelligent. Le dialogue avec le terminal sera en réalité un échange
entre deux ordinateurs : le système informatique central qui décide d’afficher
quelque chose et l’ordinateur interne dédié du terminal. Ces deux ordinateurs
se considèreront l’un l’autre comme des périphériques ordinaires de sortie et
d’entrée, transférant des caractères.
Le processeur du terminal exécute une boucle infinie consistant à attendre
un ordre d’affichage venant de l’ordinateur principal et à effectuer ensuite
l’opération correspondante : dessiner un rectangle, allumer tel pixel, effa-
cer tout l’écran, etc. Le processeur principal se contente alors d’envoyer les
quelques octets représentant l’ordre graphique correspondant.
La syntaxe de commande des périphériques de sortie graphique peut être
très élaborée : la complexité et la puissance d’expression du langage post-
script, reconnu par de nombreuses imprimantes, sont celles des langages de
programmation.
Chapitre 18
1. Interprétation et compilation
Pour étudier les deux principes d’exécution de programmes, par in-
terprétation ou par compilation, nous utilisons un petit langage impératif très
simple. Ce langage est toutefois plus riche que les langages machine dont nous
avons vu l’interprétation aux chapitres 12 et 14. Il comporte en particulier des
structures itératives et conditionnelles.
read X
read Y ! X dans l0, Y dans l1, Z dans l2
Z <-- X - Y call read ; nop
while Z add g0, o0, l0
rem signifie : call read ; nop
rem tant que Z non nul add g0, o0, l1
if Z while : subcc l0, l1, l2
rem signifie Z > 0 be endwhile ; nop
then ble else ; nop
X <-- X - Y subcc l0, l1, l0
else ba endif ; nop
Y <-- Y - X else : subcc l1, l0, l1
endif endif : ba while ; nop
Z <-- X - Y endwhile : add g0, l0, o0
endwhile call write ; nop
write X
instruction de la forme : ... ←− expr, où les pointillés doivent être rem-
placés par le nom d’une des variables prédéfinies, et où l’expression expr est
formée d’un seul opérateur, appliqué à des opérandes qui sont soit des noms
de variables, soit des notations de constantes entières positives en décimal.
La figure 18.1 donne un exemple de texte du langage L et un programme
en langage d’assemblage sparc correspondant.
lexique
MAXLIGNE : l’entier 100
numligne : le type entier sur 1..MAXLIGNE
Texte : le type séquence de caractères
{ On suppose l’existence d’opérations manipulant des textes, comme
l’égalité notée =, la différence notée =, etc. De plus on note les
constantes texte avec des guillemets. }
Ligne : le type < Mot1, Mot2, Mot3, Mot4, Mot5 : des Textes >
Prog : un tableau sur [1..MAXLIGNE] de Lignes
M1, M2, M3, M4, M5 : des Textes
CP : un entier sur 1..MAXLIGNE+1
{ Le compteur programme, c’est-à-dire le numéro de la ligne de l’ins-
truction en cours d’interprétation. }
DebBoucle : un entier sur 1..MAXLIGNE
vX, vY, vZ : des entiers
tmp1, tmp2 : des entiers ; cond : un booléen
N : le numéro de la dernière ligne du texte lu
CP ←− 1
tantque CP = N+1
M1 ←− Mot1 de Prog[CP]
selon M1 :
M1 = ”rem” : CP ←− CP + 1
M1 = ”read” :
Lire (tmp) ; AffectParNom (Mot2 de Prog[CP], tmp) ; CP ←− CP + 1
M1 = ”write” :
Ecrire (ValeurDeNom (Mot2 de Prog[CP])) ; CP ←− CP + 1
M1 = ”X” ou M1 = ”Y” ou M1 = ”Z” :
M3 ←− Mot3 de Prog[CP] ; M4 ←− Mot4 de Prog[CP]
M5 ←− Mot5 de Prog[CP]
tmp1 ←− selon M3
M3 = ”X” ou M3 = ”Y” ou M3 = ”Z” : ValeurDeNom (M3)
sinon ValeurDeNombre (M3)
tmp2 ←− selon M5
M5 = ”X” ou M5 = ”Y” ou M5 = ”Z” : ValeurDeNom (M5)
sinon ValeurDeNombre (M5)
tmp ←− selon M4
M4 = ”+” : tmp1 + tmp2
M4 = ”*” : tmp1 * tmp2
M4 = ”−” : tmp1 − tmp2
AffectParNom (M1, tmp) ; CP ←− CP + 1
M1 = ”while” :
tmp ←− ValeurDeNom (Mot2 de Prog[CP])
cond ←− (tmp = 0)
si cond alors
DebBoucle ←− CP ; CP ←− CP + 1
sinon
tantque Mot1 de Prog[CP] = ”endwhile” : CP ←− CP + 1
CP ←− CP + 1
{ on est sur la ligne qui suit la ligne du ”endwhile” }
M1 = ”endwhile” : CP ←− DebBoucle
M1 = ”if” :
cond ←− (ValeurDeNom (Mot2 de Prog[CP])) > 0
si cond alors CP ←− CP + 1
sinon
tantque Mot1 de Prog[CP] = ”else” : CP ←− CP + 1
CP ←− CP + 1
M1 = ”then” : CP ←− CP + 1
M1 = ”else” :
tantque Mot1 de Prog[CP] = ”endif” : CP ←− CP + 1
M1 = ”endif” : CP ←− CP + 1
sinon : Ecrire (”Erreur : instruction inconnue :”, M1)
1.4.4 Emulation
Nous avons vu au chapitre 12 la notion de compatibilité de familles de
processeurs. Si les deux machines sont très différentes, le constructeur fournit
un émulateur du langage machine n sur la machine n + 1. Un émulateur est
un programme, écrit dans un langage quelconque, par exemple C, et compilé
sur la nouvelle machine, avec le nouveau compilateur C. Ce programme est un
interprète du langage machine n. Le code objet des anciens programmes n’est
donc plus directement interprété par un processeur, mais par un programme,
lui-même compilé et exécuté sur un autre processeur.
C’est le cas des macintosh : Apple fournit un émulateur de 68000 parmi
les programmes du logiciel de base fourni avec les machines à PowerPC.
2.1 Un exemple en C
Nous donnons figure 18.4 un exemple de programme C décomposé en trois
fichiers : main.c qui contient le programme principal, lequel fait appel à une
fonction Fact non définie dans ce fichier-là ; fact.c qui contient la définition
complète de la fonction Fact (profil et corps) ; fact.h qui contient le profil
de la fonction Fact. Ce fichier dit d’interface est inclus dans le fichier du
programme principal, qui peut ainsi être compilé indépendamment du fichier
qui contient le corps de la fonction Fact.
Le fichier d’interface est également inclus dans le fichier qui contient la
définition complète de la fonction Fact ; cette redondance de définitions per-
met de faire vérifier au compilateur la conformité entre la version de Fact du
fichier fact.c et la version publiée dans fact.h à l’usage d’autres fichiers utili-
sateurs comme main.c. Noter toutefois que l’inclusion de fact.h dans fact.c
n’est pas obligatoire ; c’est une précaution du programmeur, pour éviter des
erreurs dues au mécanisme très rudimentaire qui sert de support à la program-
mation modulaire en C. Un langage comme Ada offre en revanche un support
complètement contrôlé.
Les paragraphes suivants détaillent la structure de l’exemple.
fichier
source outil α α1
1
outil
β
fichier
source outil α α2
2
fichier
γ
concaténation
de textes δ outil α
duire les mêmes noms pour le codage des structures de contrôle, et ces noms
seront identifiés par l’outil de fusion β.
Pour les étiquettes provenant de noms d’objets du programme source, cela
peut paraı̂tre moins contraignant : il est à la charge du programmeur de ne
pas définir deux fois la même fonction dans deux fichiers différents du même
programme. Toutefois, si l’on imagine un programme vraiment très grand, écrit
par une équipe de 50 programmeurs, il est fort probable que deux d’entre eux
auront écrit une fonction max pour des besoins locaux. Dans un langage comme
Ada, C ansi, Pascal, on peut cacher la définition de la fonction max dans une
autre fonction, et le problème est réduit : pour ces fonctions locales, le même
mécanisme de portée que pour les variables locales de procédures s’applique.
Le problème subsiste pour les fonctions principales, qui ne sont incluses dans
aucune autre.
Ces problèmes trouvent une solution dans la structure de modules des lan-
gages de programmation, implémentée de manière plus ou moins propre et
contrôlée selon les langages.
L’idée de base, que l’on retrouve dans le mécanisme rudimentaire de
définition de portée par fichier en C, consiste à permettre un regroupement
d’objets du langage dans un module. A l’intérieur d’un module, des objets
sont définis et localement utilisables. Pour être visibles de l’extérieur (dans les
autres modules), ils doivent être exportés. Il devient donc possible de définir
deux fonctions max dans deux modules différents, du moment qu’on ne les ex-
porte pas. L’interface d’un module récapitule les objets définis dans ce module
et utilisables à l’extérieur.
En C, la notion de module correspond à la structure de fichier. Ce n’est
pas le cas en Ada par exemple, où un même fichier peut contenir plusieurs
modules ou packages. En C, tout objet défini dans un fichier est par défaut
exporté. Pour cacher sa définition à l’extérieur, il faut préfixer sa déclaration
par le mot-clé static. En langage d’assemblage, c’est souvent l’inverse : toute
étiquette définie est locale au fichier, sauf exportation explicite. Dans les lan-
gages d’assemblage que nous avons utilisés dans cet ouvrage, l’exportation
explicite se fait par une directive .global (voir par exemple l’exemple donné
Figure 12.9 du chapitre 12).
Ce mécanisme rudimentaire de masquage des noms ne saurait être qualifié
de support à la programmation modulaire. Un inconvénient majeur est l’impos-
sibilité de partager un nom entre deux fichiers (ou modules) d’un programme,
sans le partager également avec tous les autres : la directive d’exportation est
tous azimuts.
NAME
cos - cosine function
SYNOPSIS
cc [ flag ... ] file ... -lm [ library ... ]
include <math.h>
double cos(double x) ;
DESCRIPTION
The cos() function computes the cosine of x, measured in radians.
RETURN VALUES
Upon successful completion, cos() returns the cosine of x.
If x is NaN or +Inf, NaN is returned.
.data
D : .long 42 ! une donnée de 32 bits initialisée à la valeur 42
.text
debut :
sethi %hi (D), %r1 ! couple d’instructions destiné
or %r1, %lo(D), %r1 ! à ranger la valeur sur 32 bits de
! l’adresse représentée par D dans r1
ld [%r1], %r2 ! chargement depuis la mémoire
! du mot d’adresse r1
! ici r2 doit contenir 42.
nop
ba debut ! branchement inconditionnel
nop
A 0000001100 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?sethi
? ? ? %hi (D), %r1
A+4 1000001000010000011 ? ? ? ? ? ? ? ? ? ? ? ?or ? %r1, %lo(D), %r1
A+8 11000100000000000100000000000000 ld [%r1], %r2
A+12 10000000000000000000000000000000 nop (add %g0, %g0, %g0)
A+16 00010000101111111111111111111100 ba debut (ba -4)
A+20 10000000000000000000000000000000 nop (add %g0, %g0, %g0)
A+24 00000000000000000000000000101010 .long 42
lexique
ModeDeCalcul : le type (reloc hi22, reloc lo10, reloc13, ...)
{ Type énuméré représentant les expressions de calcul. Les noms sont les
noms effectifs utilisés dans les systèmes à base de sparc. En anglais, trans-
lation se dit relocation }
DonnéeTranslation : le type <
position : un entier ≥ 0, mode : un ModeDeCalcul,
const : un entier >
{ La taille du trou est implicitement codée dans le mode de calcul ; la position
du trou est donnée en adresses d’instructions (donc c’est un multiple de 4)
car le codage des instructions sparc est tel que les trous sont toujours
cadrés à droite. }
D : une DonnéeTranslation
Masque22pF : l’entier ((222 − 1) × 210 )
{ 22 bits à 1 en poids Forts, 10 bits à 0 en poids faibles. Pour les détails de
construction, revoir le paragraphe 4. du chapitre 3, à propos du lien entre
l’arithmétique et les booléens. }
Masque10pf : l’entier 210 − 1
Masque13pf : l’entier 213 − 1
{ 10 (ou 13) bits à 1 en poids faibles, 22 (ou 19) bits à 0 en poids Forts }
algorithme
{ Le fichier objet translatable a été copié en mémoire à partir de l’adresse A,
instructions d’abord, données ensuite, comme sur la figure 18.8. On parcourt
les données de translation du fichier objet. }
D parcourant les données de translation du fichier :
selon D.mode :
{ On modifie un mot de 32 bits dans la mémoire, à l’adresse
A+D.position, en superposant un autre mot de 32 bits, grâce à une
opération OR bit à bit. Voir chapitre 12, paragraphe 1.4.1 }
D.mode = reloc hi22 :
MEM [A + D.position] ←− 4
MEM [A + D.position] OR (((A + D.const) ET Masque22pF) / 210 )
D.mode = reloc lo10 :
MEM [A + D.position] ←− 4
MEM[A + D.position] OR ((A + D.const) ET Masque10pf)
D.mode = reloc 13 :
MEM [A + D.position] ←− 4
MEM [A + D.position] OR ((A + D.const) ET Masque13pf
....
F ←− β (F1 , F2 )
F ←− β (F , F3 )
...
F ←− β (F , Fn )
{ Des erreurs de double définition peuvent survenir lors des fusions }
Si le fichier F contient des symboles indéfinis alors
ERREUR
sinon
Transformer F en fichier exécutable
{ ce n’est pas tout à fait le même format que les fichiers objet translatables,
voir détails ci-dessous. }
3.2.1 En-tête
Un fichier objet translatable commence par une en-tête qui constitue en
quelque sorte la carte du fichier. On y trouve en particulier l’indication sur la
taille de toutes les autres sections, qui permet d’y accéder directement par des
décalages (Cf. Chapitre 19).
On y trouve aussi la taille de la zone BSS du programme d’origine. La
section BSS des programmes en langage d’assemblage est analogue à la sec-
tion DATA, mais on ne fait qu’y demander la réservation d’une certaine zone
mémoire, sans déclarer de valeur initiale. La seule information nécessaire dans
le fichier objet est donc la taille de cette zone, alors que pour la zone DATA
il faut stocker le codage de toutes les valeurs initiales. Au moment du charge-
ment/lancement, l’allocation de mémoire est faite en tenant compte des besoins
de la zone BSS.
Enfin l’en-tête indique le point d’entrée du programme, c’est-à-dire où se
trouve l’instruction correspondant au début du programme principal, parmi
toutes les instructions de la zone TEXT. Le point d’entrée est donné comme
un décalage par rapport au début de la zone TEXT.
la forme :
X: .long 42
Y: .long 212
.long X ! utilisation absolue du symbole X
.long Y - X ! utilisation relative des symboles Y et X
A l’exécution, le mot de 32 bits situé en troisième position dans la zone des
données initialisées contiendra l’adresse effective correspondant au symbole X.
C’est une utilisation absolue de X, et l’assembleur ne peut produire le mot de
32 bits correct.
En revanche, l’expression Y - X est une utilisation relative des deux sym-
boles X et Y : la différence des adresses représentées par ces deux symboles ne
dépend pas de l’adresse de chargement du programme, et peut être calculée
lors de l’assemblage ; elle vaut 4. Une telle déclaration en zone DATA utilise
les symboles de la même manière que l’instruction de branchement ba debut
de l’exemple étudié plus haut.
lation dans le cas des symboles connus à l’assemblage, c’est-à-dire définis dans
le fichier.
Les utilisations de symboles indéfinis sont une autre source de trous dans
le code généré par l’assembleur. Ainsi une instruction de branchement relatif
ba labas produit un mot de 32 bits dont les 22 bits de poids faibles forment
un trou, lorsque le symbole labas n’est pas défini localement.
Pour repérer ce trou, et décrire comment le compléter plus tard, on utilise
également une donnée de translation. Le type d’une donnée de translation
générale est donné figure 18.12.
Noter que si la même situation se présente, mais avec une instruction uti-
lisant F de manière absolue dans le fichier 1, il y a bien identification des
symboles, mais il subsiste une donnée de translation pour cette instruction, et
elle ne sera complétée qu’au chargement.
Il y a également identification de symboles lorsque par exemple les deux
fichiers importent la même fonction, qui n’est définie dans aucun d’eux. Elle
le sera peut-être dans un troisième fichier, qui sera fusionné avec ceux-là plus
tard.
La figure 18.13 détaille l’analyse par cas selon les portées respectives des
symboles communs aux deux fichiers.
3. Format des fichiers objets translatables et édition de liens 461
d’une mémoire secondaire, il faut tenir compte des accès que l’on désire réaliser
sur ces données : si l’on a besoin de l’accès direct, un support à accès séquentiel
ne convient pas. Chacun sait qu’il est plus facile d’écouter le troisième mouve-
ment d’une symphonie sur un disque compact que sur une cassette audio.
Il existe une deuxième différence essentielle entre les données d’un pro-
gramme (qui sont stockées quelque part en mémoire vive pendant l’exécution
du programme) et les données stockées sur un support de mémoire secondaire.
Il s’agit du mécanisme d’accès aux informations par leur nom, autrement dit
du lien entre un nom externe connu de l’utilisateur et une adresse en mémoire.
Dans le cas des programmes, les informations sont rangées dans des va-
riables nommées par l’utilisateur ; la correspondance entre le nom et l’adresse
dans le tableau MEM est calculée par le compilateur, et prise en compte lors
de la fabrication du fichier exécutable, dans lequel on peut oublier les noms
(sauf si l’on désire effectuer du débogage symbolique, voir chapitre 18).
Dans le cas des données présentes sur un support de mémoire secondaire, le
nom externe est un nom de fichier, dans la terminologie usuelle. La correspon-
dance entre ce nom externe et les adresses des données du fichier sur le support
est établie par exemple lors de la création d’un fichier, par le logiciel qui s’oc-
cupe de la gestion du support. Cette correspondance est une information dont
la durée de vie doit être au moins égale à celle des données considérées : elle
doit donc être stockée sur le support lui-même. A n’importe quel autre “en-
droit” (mémoire vive de la machine, que ce soit dans un programme système
ou dans un programme utilisateur), elle aurait une durée de vie inférieure à
celle des données sur le support de mémoire.
On appelle système de fichiers l’ensemble des données stockées sur un sup-
port de mémoire secondaire (disque, bande, ...). Ces données comprennent
bien sûr les données de l’utilisateur, mais aussi des informations qu’il n’a pas à
connaı̂tre, sur l’organisation de ce support de mémoire : correspondance entre
noms externes et adresses, où reste-t-il de la place libre ?, etc.
On appelle système de gestion de fichiers (SGF dans la suite) l’ensemble
des programmes responsables de l’installation d’un système de fichiers sur un
support de mémoire secondaire. Le SGF réalise l’interface entre l’utilisateur,
qui peut désigner ses données par des noms de fichiers par exemple, et le logiciel
pilote de périphérique qui réalise les lectures/écritures effectives sur le support
de mémoire.
Notons que ces deux notions correspondent aussi bien à la gestion des
fichiers utilisateurs sur un système mono ou multi-utilisateurs ordinaire, qu’à la
manipulation des fichiers de stockage d’un SGBD (Système de Gestion de Bases
de Données). L’organisation des informations sur les supports secondaires est
toutefois plus compliquée dans le cas des SGBD, pour lesquels les contraintes
de temps d’accès sont primordiales (et le volume des données tel que l’on ne
peut pas systématiquement recopier les données en mémoire vive avant de
les traiter). Nous étudierons dans ce chapitre le cas d’un système de fichiers
1. Situation du système de gestion de fichiers 465
Adresses logiques
Le système de gestion de fichiers (SGF) :
Structuration des informations
Adressage logique
Logiciel
Adresses physiques
Circuit contrôleur de périphérique
Matériel
Disque Bande
utilisateurs ordinaire.
Nous précisons tout d’abord dans le paragraphe 1. la position du système
de gestion de fichiers, entre le matériel spécifique (disques, bandes et
leurs contrôleurs, Cf. Chapitre 16) et la vision des informations que peut
avoir l’utilisateur à travers un interprète de commandes textuel (Cf. Cha-
pitre 20). Le paragraphe 2. rappelle la notion d’adresse physique héritée
du pilote de périphérique (Cf. Chapitre 17) et définit les notions de fichier
et d’adresse logique. Le paragraphe 2.3 étudie la fonction principale d’un
système de gestion de fichiers, c’est-à-dire l’implantation des fichiers sur
les supports physiques. Le paragraphe 4. décrit comment associer aux fi-
chiers des informations comme les noms externes ou la date de création.
Finalement, nous étudions dans le paragraphe 5. quelques fonctions de
base d’un système de gestion de fichiers, comme le formatage d’un disque
ou la création de fichier.
magnétique (Cf. Chapitre 17) fournit vers les couches d’abstraction supérieures
une interface qui permet de manipuler des blocs — ou unités d’accès —
numérotés séquentiellement. Dans la suite de ce chapitre, nous appellerons
adresse physique le mécanisme de désignation non ambiguë d’une unité d’accès,
c’est-à-dire le numéro de bloc. Rappelons que le logiciel pilote de périphérique
traduit cette numérotation séquentielle en couples (numéro de secteur, numéro
de piste) pour les disques (l’unité d’accès peut même être constituée de plu-
sieurs secteurs).
Tout en haut du schéma on trouve la couche supérieure visible par l’uti-
lisateur. Les informations manipulées sont des noms de fichiers (qui reflètent
éventuellement la structure hiérarchique de l’ensemble des fichiers). On peut
trouver également d’autres informations associées à un fichier, comme les droits
d’accès des différents utilisateurs, la date de création, le nom du programme
qui l’a créé, etc.
Le SGF est la couche intermédiaire : il assure la correspondance entre les
noms de fichiers et la position des données de ces fichiers sur les blocs du
support de mémoire.
Lorsque l’utilisateur tape editer /users/machin/toto.c, le programme
editer fait appel aux fonctions du SGF pour retrouver le lien entre ce nom
externe et la position des données sur le support de mémoire, et aux fonctions
offertes par le logiciel pilote de périphérique pour réaliser la lecture effective
de ces données.
Nous détaillons au paragraphe 5. les principales fonctions du SGF acces-
sibles aux programmes de l’utilisateur.
opérations usuelles que l’on effectue sur un fichier source (édition, impression,
...) l’interprètent comme une suite de caractères (c’est-à-dire une suite d’oc-
tets). De même, le fichier objet peut être considéré comme une suite de bits,
ou comme une suite d’octets. Les images ou les contenus de bases de données
constituent d’autres types de fichiers.
Les fichiers texte sont intrinsèquement séquentiels, puisque le type Texte
manipulé par les programmes est défini comme une séquence de caractères
(l’ordre des éléments fait partie du type). Inversement les fichiers de bases de
données relationnelles correspondent au type de données ensemble de n-uplets.
Un fichier de personnes peut ainsi être constitué de triplets formés d’un
nom, d’une date de naissance et d’un nom de ville. Il n’y a pas de structure
séquentielle attachée à ce type de données. Les n-uplets peuvent être désignés
de manière non ambiguë par une clé. Dans la théorie de l’algèbre relationnelle,
la clé — ou identifiant — d’une relation est un sous-ensemble des champs tel
que, pour une valeur des champs de la clé, le n-uplet est unique. Dans le fichier
de personnes donné en exemple, on peut décider d’utiliser le nom comme clé.
On appelle adresse logique le mécanisme de désignation non ambiguë d’un
enregistrement du fichier. Si le fichier comporte une structure séquentielle, c’est
en général un numéro par rapport au début ; dans le cas contraire, on utilise
la notion d’identifiant fournie par la structuration des données : une valeur de
la clé permet de désigner un n-uplet sans ambiguı̈té.
non séquentiel.
Reprenons l’analogie avec les disques et cassettes audio : il est plus facile
d’écouter la douzième chanson sur un disque compact que sur une cassette
audio, mais, lorsqu’on recopie une cassette sur une autre, on n’a besoin de
l’accès direct ni sur la source, ni sur la copie.
Dernier point important : pourquoi se préoccuper d’accès direct aux
données stockées sur un support de mémoire secondaire ? On pourrait en effet
imaginer une manière fort simple de manipuler les fichiers : on commence tou-
jours par le recopier entièrement en mémoire vive (et cela nécessite une lecture
séquentielle), puis on effectue tout type de modification, puis on le recopie sur
le support de mémoire secondaire (et cela constitue une écriture séquentielle).
Dans ce cas les seuls types d’accès nécessaires sont séquentiels.
Cette approche est applicable à l’édition de petits fichiers texte. Pour
d’autres types d’application c’est hors de question : tri de gros fichiers, accès
à une base de données, ...
Pour assurer des accès directs parmi les données d’un fichier, il faut donc
prévoir l’implantation en conséquence.
Accès direct sur support séquentiel C’est un cas peu réaliste. Sur tout
support séquentiel on dispose d’une opération de retour au début (rembobinage
d’une bande magnétique), mais passer d’un élément d’adresse n à un élément
d’adresse m en rembobinant, puis en avançant de m, ne peut pas avoir des
performances comparables à celles d’un véritable accès direct.
Accès direct sur support à accès direct Le fichier peut être considéré
comme une séquence de n-uplets plus ou moins complexes, numérotés par les
adresses logiques.
On trouve dans tout bon ouvrage d’algorithmique une discussion sur les
mérites respectifs de la représentation des séquences dans des tableaux ou des
séquences chaı̂nées. Dans un tableau, l’ordre des éléments de la séquence est
implicite : c’est l’ordre des indices ; dans une séquence chaı̂née, l’ordre des
éléments est représenté de manière explicite : chaque élément “pointe” sur
son successeur (et/ou sur son prédécesseur). Notons que les éléments de la
séquence sont de toute façon dans le tableau MEM : la différence entre les
deux approches est donc entre une implantation contiguë et une implantation
dispersée. .
Avec la solution tableau on occupe le minimum de mémoire ; avec la solu-
tion chaı̂née, on ajoute un “pointeur” par élément (c’est-à-dire une adresse
mémoire, de l’ordre de 32 bits). La comparaison sur le temps nécessaire
pour une opération d’insertion tourne en revanche à l’avantage de la solution
chaı̂née : il suffit de raccrocher deux ou trois pointeurs et le tour est joué ; dans
un tableau il faut ménager une place en décalant des éléments, c’est-à-dire en
les recopiant d’une case dans une autre. Comme d’habitude en informatique, les
gains en place se font au détriment du temps, et vice-versa. Notons également
qu’avec une implantation contiguë on dispose de l’accès direct à un élément
par son numéro ; on perd cette propriété avec une implantation dispersée.
Imaginons le cas d’une séquence de bits, stockée en utilisant une solution
mixte contigüe/dispersée : on peut chaı̂ner entre eux des octets, auquel cas le
rapport entre informations utiles et informations de chaı̂nage est assez mau-
vais : un pointeur occupe de l’ordre de 4 octets, et il en faut 1 par octet utile.
En choisissant la taille des blocs chaı̂nés, on règle le rapport entre informations
utiles et informations de chaı̂nage.
Appliquons le raisonnement au cas de l’implantation des fichiers.
Dans une implantation contiguë, les emplacements de deux enregistrements
consécutifs quelconques du fichier sont eux-mêmes consécutifs à l’intérieur
d’une même unité d’accès, ou situés dans deux unités d’accès de numéros
consécutifs. La structure de séquence du fichier est représentée grâce à la
séquence des adresses physiques. Une insertion ou suppression d’enregistre-
ment dans le fichier demande un décalage des éléments présents, ce qui peut
être assez coûteux.
Dans une implantation dispersée, deux enregistrements consécutifs du fi-
chier peuvent être placés à des positions quelconques sur le disque. Pour re-
constituer la structure séquentielle, on peut chaı̂ner les éléments entre eux, ou
utiliser une table d’implantation (voir paragraphe 3. ci-dessous). L’insertion
ou la suppression d’un élément demande alors seulement la réorganisation du
chaı̂nage ou de la table d’implantation, mais jamais le déplacement des enre-
gistrements du fichier sur le disque. La solution par chaı̂nage est coûteuse en
taille (une adresse de suivant pour chaque élément du fichier), et impose un
470 Système de gestion de fichiers
Table d’implantation :
dans l’unité d’accès de numéro 1.
1 2 3
...
T
Fig. 19.2 – Implantation dispersée d’un seul fichier de petite taille : l’unité d’allocation
est égale à deux unités d’accès, et la table d’implantation est située sur une
unité d’accès. Elle donne T adresses physiques d’unités d’accès, qu’il faut
interpréter comme les adresses de début des blocs du fichier, chacun de taille
égale à l’unité d’allocation.
3. Implantation dispersée sur un disque 473
Table secondaire
1
Table primaire
T’ T
T+1
Fig. 19.3 – Implantation dispersée d’un seul fichier de taille moyenne : l’unité d’allocation
correspond à deux unité d’accès. La table d’implantation primaire est située
sur une unité d’accès. Elle donne tout d’abord T adresses physiques d’unités
d’accès, qu’il faut interpréter comme les adresses de début de blocs du fichier.
Elle donne également, dans sa case d’indice T + 1, l’adresse physique d’une
unité d’accès qui contient une table secondaire. Cette nouvelle table donne T
adresses physiques d’unités d’accès, qu’il faut interpréter comme les adresses
de début des blocs du fichier. Tous les blocs du fichier sont de taille égale à
l’unité d’allocation
474 Système de gestion de fichiers
Les octets formant le contenu du fichier sont stockés sur les unités d’accès
d’adresses physiques :
tab-imp[0]+0, tab-imp[0]+1, ..., tab-imp[0]+UAlloc −1,
tab-imp[1]+0, tab-imp[1]+1, ..., tab-imp[1]+UAlloc −1,
...
désigné par un nom ; l’ouverture consiste à donner accès à toutes les informa-
tions stockées dans le fichier.
Ensuite on utilise les actions de lecture et d’une action d’écriture d’une
unité d’accès fournies par le pilote de périphérique associé au disque (Cf. Pa-
ragraphe 3. du chapitre 17).
recopié et les informations non liées à l’implantation du fichier sur disque sont
initialisées.
1. Démarrage du système
Nous considérons un système simple qui comporte de la mémoire morte, de
la mémoire vive et un disque dur.
Au démarrage du système, c’est-à-dire à la mise sous tension du dispositif
matériel et logiciel, on peut supposer que la réalisation matérielle charge la
valeur 0 dans le compteur programme PC. Le processeur commence donc à
interpréter le contenu de la mémoire à l’adresse 0. Cette adresse correspond à
de la mémoire morte, qui contient l’amorce du système.
Cette amorce est constituée du code de la procédure de démarrage ; d’un
pilote de disque rudimentaire (c’est-à-dire un ensemble de procédures d’accès
au disque) et d’un embryon de système de gestion de fichiers capable de re-
trouver sur le disque une image du système d’exploitation complet et de la
recopier en mémoire vive.
Le système complet comporte le système de gestion de fichiers complet
(décrit au chapitre 19), les pilotes de périphériques complets (décrits au
chapitre 17) dont le pilote du disque, le code de la procédure de charge-
ment/lancement de programmes, que nous étudions en détail au paragraphe 2.
ci-dessous.
2.1.2 Le résultat
MEM
MEM
0 code de ROM
l’amorce
max
SP
(a) (b)
Fig. 20.1 – (a) : Contenu de la mémoire après la première étape du démarrage.
(b) : Contenu détaillé de la pile installé par l’appelant de la procédure de
chargement/lancement : le nom du programme est mor, les paramètres à lui
transmettre sont -d, -R et toto/tutu.
490 Démarrage du système, langage de commandes et interprète
...
TDB (R)
Adresses
ZoneP
TDB (P) faibles
Adresses
fortes
SP
Variables locales
de P
FP
Sauv. FP de R
...
492 Démarrage du système, langage de commandes et interprète
de fichier d’après son nom (Cf. Chapitre 19). Les paramètres à lui passer sont :
l’adresse du nom de fichier, le mode d’ouverture (ici “lecture”), l’adresse de la
variable PFichier. La procédure d’ouverture peut échouer, et rendre un code
d’erreur dans un registre ; dans ce cas la procédure de chargement se termine,
en conservant ce code de retour (voir dernier point).
Lorsque l’ouverture du fichier s’est bien passée, la variable PFichier est
pertinente, et peut servir à réaliser les accès suivants. L’étape suivante est
l’appel de la procédure du SGF qui permet de lire une portion du fichier de
la taille de EnTete, à partir du début du fichier. Les paramètres à lui passer
sont : l’adresse de PFichier, l’adresse de EnTete. Si tout se passe bien, la va-
riable EnTete contient ensuite la description des zones du fichier exécutable,
ainsi qu’un marqueur qui indique la nature du fichier. Si ce n’est pas un fi-
chier exécutable, cela constitue un nouveau cas où la procédure de chargement
échoue, en rendant un code d’erreur. Si le fichier est bien un fichier exécutable,
on peut poursuivre.
Le code de la procédure de chargement/lancement consiste ensuite à calcu-
ler la taille de la zone mémoire nécessaire à l’installation du code et des données
du programme Q. L’en-tête du fichier exécutable donne les tailles respectives
des zones TEXT, DATA et BSS du programme. La variable Taille est affectée
à la somme de ces tailles (éventuellement arrondie au multiple de 4 ou de 8
supérieur, si ce n’est pas déjà fait dans le fichier exécutable, pour satisfaire à
des contraintes d’alignement en mémoire vive).
Il faut ensuite allouer une zone mémoire pour le programme à lan-
cer et déterminer ainsi l’adresse de chargement. Nous avons vu au para-
graphe 2.2.2 que la zone de mémoire utilisée pour installer le code des pro-
grammes que l’on charge est gérée en pile, dans notre système simple. Il
suffit donc de déplacer le pointeur de début de la zone de mémoire libre, à
partir de son ancienne position. Cette position est connue et vaut, pendant
l’exécution de C, ZoneP = MEM [MEM[FP] + Δ]]. Il suffit donc de calculer
ZoneQ ←− ZoneP + Taille.
La figure 20.2-(c) montre le résultat de l’allocation : la zone disponible pour
Q est comprise entre les adresses ZoneP incluse et ZoneQ exclue.
La procédure C poursuit en recopiant le fichier exécutable en mémoire vive,
à partir de l’adresse ZoneP, vers les adresses fortes, c’est-à-dire entre ZoneP
incluse et ZoneQ = ZoneP + taille exclue. Elle applique ensuite l’algorithme de
translation des adresses. Pour cela il faut lire dans le fichier toujours ouvert la
zone des données de translation TEXT et la zone des données de translation
DATA. Le fichier ne sert plus à rien ensuite, et peut donc être refermé. Le
mot mémoire qui contient l’adresse du nom du fichier à charger (juste sous
l’adresse de retour dans l’appelant du chargeur, marqué d’une ’*’ sur la figure)
peut être écrasé, puisque le nom ne sert plus à rien. On y range l’adresse ZoneQ
(Cf. Figure 20.2-(c)).
L’un des champs de l’en-tête donne le décalage Décal du point d’entrée du
programme Q par rapport au début de sa zone TEXT : c’est l’adresse relative
494 Démarrage du système, langage de commandes et interprète
Il ne reste plus qu’une étape pour atteindre l’état décrit par la figure 20.2-
(d), dans lequel le programme Q est installé et en cours d’exécution. On s’est
ramené à l’invariant décrit par la figure 20.2-(a), Q peut lui-même charger et
lancer un autre programme.
Une instruction type rts du langage machine 68000 suffit (γ) : son exécution
dépile l’adresse absolue du point d’entrée du programme Q dans le compteur
programme. On entre donc dans le code de la procédure principale du pro-
gramme Q avec SP pointant sur l’adresse de retour dans P, et FP pointant sur
la base de l’environnement de P. Le prologue de la procédure principale de Q
installe la sauvegarde du pointeur de base, déplace FP, puis déplace SP pour
ménager la place des variables locales de la procédure principale de Q.
Les paramètres pour Q, qui lui avaient été transmis par P via C, sont
disponibles dans la pile à l’endroit habituel, à partir de FP+3× 4 (les adresses
étant stockées dans 4 octets).
3. Programmation de l’interprète
de commandes
L’interprète de commandes est le programme lancé par la procédure globale
de démarrage du système. Il est fait pour être actif pendant toute la durée de
vie du système ; quand sa procédure principale se termine, le contrôle revient
dans la procédure de démarrage (Cf. Paragraphe 1.3).
496 Démarrage du système, langage de commandes et interprète
Lexique
Fin : un booléen ; L : un tableau de caractères
NomProg : un tableau de caractères
Param : un tableau de tableaux de caractères
NbParam : un entier ≥ 0
Algorithme
Fin ←− faux
répéter jusqu’à Fin
{ Lecture d’une ligne de commande par appel du pilote de clavier. }
L ←− ...
{ Analyse lexicale et syntaxique de la ligne L : fournit NomProg, Param et
NbParam }
si NomProg = ”Quitter” alors
Fin ←− vrai
sinon
{ Passage de paramètres au chargeur/lanceur : les paramètres à desti-
nation du programme à lancer d’abord, le nom du programme à lancer
en dernier. }
i parcourant 1..NbParam : Empiler la chaı̂ne Param[i]
Empiler la chaı̂ne NomProg
i parcourant 1..NbParam :
Empiler les adresses des chaı̂nes précédentes
empiler NbParam
empiler adresse de NomProg
appel de la procédure charger lancer
{ On revient directement là quand le programme chargé a terminé. }
dépiler paramètres de charger lancer
{ Fin de l’interprète. On retourne dans l’appelant du chargeur qui l’avait
installé, c’est-à-dire le programme de démarrage, qui s’occupe de terminer
le système proprement. }
Lexique
Fin : un booléen
L : un tableau de caractères
NomProg : un tableau de caractères
Param : un tableau de tableaux de caractères
CommandesIntrinsèques : un ensemble de tableaux de caractères
{ Les noms des commandes directement traitées dans la boucle d’in-
terprétation, dont ”Quitter” }
NbParam : un entier ≥ 0
Algorithme
{ Diverses initialisations }
Fin ←− faux
répéter jusqu’à Fin
{ Lecture d’une ligne par appel du pilote de clavier. }
L ←− ...
{ Analyse lexicale et syntaxique de la ligne L : fournit NomProg, Param
et NbParam }
si NomProg ∈ CommandesIntrinsèques
selon NomProg
NomProg = ”comm1” : ...
NomProg = ”comm2” : ...
NomProg = ”Quitter” : Fin ←− vrai
...
sinon
{ Séquence d’appel du chargeur/lanceur, voir paragraphe 3.1 ci-
dessus. }
{ Fin de l’interprète. }
dant est chargé en mémoire, exécuté, rend un résultat, et ce résultat est affiché
par l’interprète de commandes.
Il est important de noter que, si le SGF n’offre pas la notion de répertoire
courant, il est impossible de l’introduire au niveau de l’interprète de com-
mandes. L’information sur le répertoire courant devrait être une variable du
programme interprète, et les fonctions de consultation et modification de cette
variable des commandes intrinsèques. La prise en compte d’une notion de
répertoire courant devrait être assurée par l’algorithme de l’interprète, qui de-
vrait préfixer systématiquement les noms de fichiers non absolus (ceux qui ne
commencent pas par un caractère slash, dans la syntaxe unix) par la chaı̂ne
de caractères répertoire courant. Mais l’interprète de commandes n’a aucun
moyen de savoir lesquels, parmi les arguments d’une commande, sont des noms
de fichiers. Seul le programme lancé sait lesquels de ses paramètres sont des
noms de fichiers, et pour ceux-là il appelle les fonctions d’accès du SGF, les-
quelles peuvent préfixer le nom par le répertoire courant. La commande cd
(change directory) des interprètes de unix usuels ne peut donc pas être une
commande intrinsèque : elle fait appel à la fonction correspondante du SGF.
En revanche, il est possible de gérer, dans l’interprète de commandes, une
pile de répertoires. On introduit alors empiler et dépiler, comme spécifiés
au chapitre 4, paragraphe 5.1, sous forme de commandes intrinsèques. Par
exemple, dans l’interprète csh de systèmes unix, ces commandes s’appellent
pushd et popd. Une telle commande met à jour la variable pile de l’interprète
de commandes, et appelle la fonction SetRepCour du SGF.
Lexique
...
Préfixes : un tableau de tableaux de caractères
{ Les préfixes possibles pour un nom de fichier correspondant à une
commande }
NbPréfixes : un entier ≥ 0
ok : un booléen ; j : un entier
Algorithme
{ Diverses initialisations dont celle du tableau Préfixes et de la variable
NbPréfixes }
...
répéter jusqu’à Fin
ok ←− faux ; j ←− 1
tant que (non ok) et (j ≤ NbPréfixes)
Empiler les paramètres à destination du programme à lancer
Empiler la chaı̂ne Préfixes[j] & ”/” & NomProg
{ On a empilé le nom du programme à lancer. & est l’opérateur
de concaténation de chaı̂nes de caractères }
{ Empiler les adresses des chaı̂nes précédentes, le nombre de pa-
ramètres et l’adresse du nom du programme à lancer }
Appel du chargeur/lanceur
{ Si le chargeur ne trouve pas le fichier demandé, il rend un code
d’erreur FICHIER NON TROUVE (Cf. Paragraphe 2.1.2). }
si (non ok)
alors Ecrire (”Commande non trouvée”)
sinon
{ le programme a été exécuté, dépiler les paramètres du char-
geur/lanceur }
{ Fin de l’interprète. }
foreach i (‘ls‘)
# pendant l’exec. de la boucle, la variable $i parcourt
# tous les fichiers obtenus par la commande ls.
set j=‘basename $i .fig‘ # recuperer nom de base, sans extension
if ($i == $j) then # le fichier ne se termine pas par .fig
echo $i " : fichier ignore"
else
echo -n "[fichier " $i "traite ..."
... commandes portant sur le fichier de nom $i
echo "]"
endif
end
2. Scrutation
En fait tout se ramène à un problème de scrutation. De façon intuitive, dans
un système complexe il va falloir scruter quelques événements intérieurs ou
extérieurs, c’est-à-dire consulter une information de temps en temps, et prévoir
un branchement à un traitement adéquat si elle a une valeur particulière. Cette
information peut être liée au temps qui passe ou à une infraction aux droits
d’accès par exemple. Cela peut permettre de gérer les aspects de partage de
temps, les aspects de protection et de réagir à des erreurs.
Observons tous les niveaux où il est possible d’insérer une action entre
des instructions pour assurer une régularité de la scrutation. On trouve
grossièrement trois niveaux : les programmes en langage de haut niveau des uti-
lisateurs ; les programmes en assembleur produits par compilation ; l’interprète
du langage machine.
On n’a pas vraiment le choix : la scrutation doit être effectuée le plus bas
possible.
Il est intéressant de réaliser la scrutation une seule fois, de ne pas faire
confiance aux divers programmes utilisateurs pour ça, et de faire en sorte
que les couches de niveau le plus haut soient le plus possible indépendantes
de ce mécanisme de scrutation ; en effet le programmeur de haut niveau n’a
parfois même pas connaissance des événements à observer. Il faut donc que la
scrutation soit prévue dans le processeur, et pour cela il faut intervenir dans
l’algorithme d’interprétation des instructions.
On sait intégrer la scrutation dans l’interprète de langage machine sans trop
le ralentir ce qui n’est pas le cas si on l’intègre dans les couches supérieures.
Le coût en temps de la scrutation est nul ou dérisoire en la réalisant au niveau
de l’interprète de langage machine.
Notons que la scrutation matérielle n’ajoute pas d’états au graphe de
contrôle du processeur, seulement des transitions. Par du matériel, un
éclatement n aire après un état est réalisé de la même façon qu’un éclatement
binaire. Ce n’est pas le cas pour les systèmes programmés ou microprogrammés.
Le fait de placer la scrutation dans l’interprète de langage machine permet
d’avoir un certain contrôle sur la fréquence à laquelle se fait la scrutation. Au
contraire si on le fait dans les programmes utilisateurs, on ne maı̂trise pas bien
508 Motivations pour une plus grande complexité
4. Plan de la suite
Pour inclure la gestion de mécanisme d’interruptions dans le processeur il
faut remettre en cause l’interprète du langage machine.
Dans la suite, nous étudions :
– le mécanisme d’interruption lui-même : principe et résolution des problèmes
que son introduction pose, en particulier les problèmes de relation de temps
entre le processeur et le monde extérieur.
– la définition des fonctions et du fonctionnement d’un système complexe
comme défini plus haut, en utilisant ce nouveau mécanisme.
Dans le chapitre 22 nous présentons un mécanisme d’interruption simple.
On étudie la modification de l’interprète de langage machine, et on présente
les problèmes liés. On introduit pour cela une machine simple comportant un
seul fil d’interruption. Cela nous permet de montrer que ce mécanisme d’in-
terruption permet déjà un partage de temps élémentaire entre un programme
principal et un traitant d’interruption. Nous étudions le cas typique de la pen-
dule : le processeur est partagé entre deux activités, et les instructions de
l’un et l’autre programmes sont entrelacées de manière non maı̂trisable par le
programmeur.
La lecture de ce chapitre est suffisante pour comprendre les aspects d’archi-
tecture matérielle liés à l’introduction du mécanisme d’interruption. La suite
constitue une introduction aux couches basses des systèmes d’exploitation mul-
titâches et multi-utilisateurs.
Le partage de temps rudimentaire vu au chapitre 22 est limité au cas
de deux programmes, dont l’un est suffisamment court. Dans le cas général
(par exemple lorsqu’aucun des programmes ne s’arrête !), il faut inventer un
mécanisme un peu plus sophistiqué : la notion de processus et le partage de
temps, géré par le traitant d’interruption. C’est une brique de base importante
pour la construction de systèmes complexes. C’est l’objet du chapitre 23.
Dans le dernier chapitre (24) nous étudions le fonctionnement détaillé
des interruptions et leurs utilisations. Nous complétons le processeur du cha-
pitre 22 pour traiter les aspects liés à la gestion d’interruptions multiples. Nous
évoquons les problèmes de protection entre les diverses activités se déroulant
dans une machine. Nous montrons enfin que le mécanisme des interruptions
permet d’optimiser la gestion des entrées/sorties présentée au chapitre 16.
Chapitre 22
Le mécanisme d’interruption
BusA
Rinst,
Regdo,T,.. REtat PC,SP,.. Opérateur
BusB
BusC
données vers la mémoire font aussi N bits. Les adresses de mots sont des
multiples de 4. Les registres sont identifiés par un numéro ou un nom. Les
noms sont principalement une facilité mnémotechnique. Les instructions ont
3 opérandes au plus. Comme sur le sparc le registre de numéro 0 contient
la constante 0. Les registres apparaissent sur la figure comme Regdo. SP vaut
pour Stack Pointer ; il est utilisé comme pointeur de pile. PC vaut pour Program
Counter ; c’est le compteur programme. Le programmeur peut les manipuler
par des instructions spéciales.
Le processeur comporte un registre d’état REtat dont le contenu est acces-
sible en permanence par la partie contrôle.
Tous les registres, y compris le registre d’état, peuvent être transmis via
les bus A et B vers l’opérateur. La sortie de l’opérateur peut être transmise
dans n’importe quel registre via un bus C. L’opérateur est doté de toutes les
opérations nécessaires : extraction des p bits de poids faible, génération de
constantes, fabrication des bits d’état Z, N, C, V, etc.
Des commutateurs entre le bus données bidirectionnel vers la mémoire et
les bus B et C permettent les communications dans les deux sens : mémoire
vers registres et registres vers mémoire.
Certains registres ne sont pas directement accessibles par le programmeur
dans les instructions : un registre particulier, connu sous le nom de Registre
Instruction Rinst, dont le contenu est accessible en permanence dans la partie
contrôle du processeur ; un registre tampon général T sert d’intermédiaire de
stockage de valeurs temporaires ; un registre TA sert de Tampon d’Adresse,
son contenu est transmis sur le bus adresses.
1. Architecture d’un processeur pour la multiprogrammation 513
reset
PC ⇐= 0
PC ⇐= PC+4 TA ⇐= PC
Inst 1 mot RInst ⇐= M[TA] Inst 2 mots
op reg
PC ⇐= PC+4 TA ⇐= PC
rts
op cst T ⇐= M[TA] brF
jsr
reg ⇐= (T ou reg)*reg TA ⇐= T + rega TA,SP ⇐= SP-4
st
M[TA] ⇐= PC
TA ⇐= SP SP ⇐= SP + 4 M[TA] ⇐= regb ld jmp,brV
PC ⇐= M[TA] regb ⇐= M[TA] PC ⇐= T+(reg ou PC)
Fig. 22.2 – Graphe de contrôle du processeur, première version. Certains rectangles com-
portent 2 lignes distinctes, correspondant à deux états successifs, donc à deux
microactions consécutives. Les deux états seraient séparés par une transition
portant sur le prédicat toujours vrai.
basculement
de départ TA,SP ⇐= SP-4
M[TA] ⇐= PC reset
TA ⇐= 44
TA ⇐= M[TA]
PC ⇐= M[TA] PC ⇐= 0
PC ⇐= PC+4 TA ⇐= PC
Inst 1 mot RInst ⇐= M[TA] Inst 2 mots
rts, rti ...
TA ⇐= SP SP ⇐= SP+4 op
PC ⇐= M[TA]
autres
non IRQ ...
reg ⇐= (T ou reg) * reg
2.3 Commentaires
Le déroulement envisagé précédemment soulève plusieurs questions, notam-
ment de vocabulaire. Nous allons les envisager.
1. Quel est le nom de ce mécanisme ? Mécanisme d’interruption. On dit que
le programme en cours d’exécution a été interrompu. Le sous-programme
traitant s’appelle un traitant d’interruption.
2. Le départ vers le sous-programme traitant est provoqué par un
événement matériel (le niveau 1 sur le fil IRQ), le retour de sous-
programme est provoqué par un événement logiciel (l’exécution de l’ins-
truction rti). Cette dissymétrie est-elle normale ? Oui. C’est là toute
l’astuce de ce mécanisme. On parle de basculement de départ et de bas-
culement de retour. On dit souvent départ en interruption, retour d’in-
terruption.
3. Le basculement de retour fait temporellement partie du traitant, mais
pas le basculement de départ. C’est une convention plutôt qu’autre chose.
Le basculement de départ n’est ni dans le programme interrompu ni dans
le traitant. Il est temporellement entre les deux.
4. Que se passe-t-il si il n’y a pas d’instruction rti dans traitant ? Rien
de spécial. Le processeur continue d’exécuter des instructions. Il n’y aura
jamais de retour au programme interrompu. Il s’agit évidemment d’une
erreur analogue à l’absence d’un rts en fin d’un sous-programme.
518 Le mécanisme d’interruption
Rinst,REtat adresses
Adresses
Const : 44 6000
PO Données
1000
PC 2000 Prog1
Calcul IRQ
état 3000
Calcul des suivant TRAITANT
commandes 3500 rti
E 4000
t Clock, Reset 5000 Pile
a
t
6000 3000
L/E
Processeur Mémoire
TRAITANT
basculements
départ retour
Prog1
IRQ
temps
Fig. 22.5 – Diagrammes d’évolution des valeurs de PC et SP. Une tranche de temps ver-
ticale pleine correspond à l’exécution d’une instruction. La tranche verticale
vide correspond au basculement de départ. Les basculements de départ et de
retour sont mis en évidence. Arbitrairement certaines instructions sont sur 1
mot, d’autres sur 2.
2. Introduction d’un mécanisme de scrutation élémentaire 519
Fig. 22.6 – Graphe de contrôle avec tout le traitement du mécanisme d’interruption. Les
instructions non liées aux interruptions ne figurent plus.
3. Un exemple détaillé d’utilisation : mise à jour de la pendule 523
.data
secondes .
:long 0
minutes :.long 0
heures : .long 0
.text
plus1sec /
:* utilise R4 comme registre de travail */
ld [secondes], R4 /* ou [R0 + secondes] */
add R4, 1, R4 /* secondes = secondes +1 */
st R4, [secondes]
subcc R4, 60, R0 /* si sec < 60 : terminé */
blu retour
st R0, [secondes] /* secondes = 0 */
ld [minutes], R4
add R4, 1, R4 /* minutes = minutes +1 */
st R4, [minutes]
subcc R4, 60, R0 /* si min < 60 : terminé */
blu retour
st R0, [minutes] /* minutes = 0 */
ld [heures], R4
add R4, 1, R4 /* heures = heures +1 */
st R4, [heures]
subcc R4, 24, R0 /* si heures < 24 : terminé */
blu retour
st R0, [heures] /* heures = 0 */
retour : rts
while (1)
{ /* attendre une impulsion sur le signal */
while (signal == 0) ; /* boucle vide ; sortie si signal = 1 */
while (signal == 1) ; /* boucle vide ; sortie si signal = 0 */
/* il y a bien eu une impulsion. */
plus1sec () ;
}
SecondesDébut= secondes ;
MinutesDébut = minutes ;
HeuresDébut = heures ;
CALCUL ; /* Le calcul proprement dit */
SecondesFin = secondes ;
MinutesFin = minutes ;
HeuresFin = heures ;
Durée = SecondesFin - SecondesDébut
+ 60 * (MinutesFin − MinutesDébut) ;
+ 3600 * (HeuresFin − HeuresDébut) ;
traitant :
Prologue :add sp, -4, sp
st R4, [sp] /*empiler Reg de travail*/
Appel : /* utilise le Reg de travail R4 */
jsr plus1sec
Epilogue ld [sp] , R4
add sp, +4, sp /*dépiler Reg de travail*/
rti
traitant
calcul
IRQ
t
i d1 r1 d2 r2 d3 r3 f
traitant
calcul
IRQ
traitant
calcul
IRQ
t
Fig. 22.13 – L’impulsion du signal d’interruption, trop longue, est comptabilisée 4 fois.
IRQ
H
t
Fig. 22.14 – L’impulsion d’une durée de 4 cycles d’horloge du processeur est prise en
compte si l’instruction en cours est add et ignorée parce que trop courte
dans le cas de jsr. Sur cette figure, le départ en interruption entre les deux
instructions add n’est pas représenté.
heures 03 04
minutes 59 00
secondes 59 00
traitant
calcul
IRQ
t
s = 59 m=0 h=4
Supposons que l’on réalise un transfert de 1000 francs d’un compte ban-
caire X crédité de 10000 francs vers un compte bancaire Y crédité de 2000
francs. Le transfert consiste à débiter X de 1000 francs puis à créditer Y de
la même somme. De l’état initial (10000,2000), le couple (X,Y ) passe par un
état transitoire (9000,2000) pour atteindre l’état final (9000,3000).
On dit que l’opération de transfert est atomique, si un programme
s’exécutant en concurrence avec celui qui réalise le transfert peut observer
(X,Y ) dans l’état initial ou dans l’état final, mais pas dans l’état transitoire
(9000,2000).
Dans notre exemple de la pendule, le programme de calcul peut lire une
heure erronée. Supposons qu’une interruption arrive à 3h 59mn 59s où le pro-
gramme termine son calcul et lit l’heure courante pour en calculer la durée
(figure 22.15).
Le scénario suivant montre que la lecture de l’heure courante n’est pas
atomique et peut rendre une valeur erronée.
Le programme de calcul lit les secondes (Instruction SecondesFin =
secondes) et trouve 59. L’interruption est prise en compte juste après et fait
passer l’heure courante à 4 :00 :00. Au retour d’interruption, le programme
interrompu poursuit sa consultation de l’heure courante et lit les minutes (00)
et les heures (4). L’heure obtenue par le programmme n’est ni 3 :59 :59, ni
4 :00 :00, mais 4 :00 :59. C’est une erreur. Le même genre de problème peut
être mis en évidence avec deux mises à jour concurrentes. Sur un ordinateur
simple à un seul processeur, le remède consiste à suspendre la prise en compte
des interruptions pendant l’accès aux variables partagées pour le rendre ato-
mique. C’est la principale utilité des instructions clri et seti que nous avons
décrites au paragraphe 2.5.
530 Le mécanisme d’interruption
5. Exercices
E22.1 : Choix de l’adresse de début du traitant
Dans le basculement de départ apparaı̂t une double indirection pour l’obtention
de l’adresse de début du traitant. Typiquement de la mémoire morte corres-
pondrait à l’adresse 44, et de la mémoire vive à l’adresse 6000. L’utilisateur
peut ainsi écrire à l’adresse 6000 l’adresse de son choix (ici 3000) où commence
le traitant.
Que faut-il modifier dans le graphe de contrôle si l’on suppose qu’un traitant
débute nécessairement à l’adresse 36848 ? Cela offre-t-il la même souplesse
d’emploi ? Comment pourrait-on modifier la machine pour que le traitement
du IRQ soit (doublement) indirectement référencé par l’adresse 44 et celui du
swi par l’adresse 48 au lieu de l’être tous les deux par 44 (Cf. Figure 22.6) ?
E22.2 : Et en vrai ?
Se procurer dans les bibliothèques et librairies (papier ou électroniques) les
modes d’emploi détaillés de processeurs simples. Etudier leur système d’inter-
ruptions. Prolonger avec des processeurs complexes. Prolonger sur des ordina-
teurs.
Chapitre 23
1. Principe et définitions
Les systèmes dits multitâches sont capables d’exécuter plusieurs pro-
grammes à la fois. Ils sont très utiles et permettent à plusieurs utilisateurs
de travailler en même temps sur un ordinateur. Ils autorisent également un
utilisateur à faire plusieurs choses à la fois, par exemple éditer un fichier pen-
dant la compilation d’un autre fichier et lire en même temps l’heure affichée
532 Partage de temps et processus
par un troisième programme. Nous restons toutefois ici dans le contexte des
machines à un seul processeur.
Un programme est une entité statique composée d’instructions décrivant
une suite d’opérations à réaliser pour aboutir au résultat recherché. A chaque
exécution d’un programme correspond une activité et un processus associé dans
les tables du système d’exploitation, créés lors du lancement du programme et
détruits quand l’exécution du programme se termine.
Il ne faut pas confondre programme avec processus ou activité. Si un utili-
sateur lance une compilation et une édition, il y a deux activités et le système
d’exploitation crée deux processus exécutant deux programmes différents (le
compilateur et l’éditeur). A deux compilations simultanées correspondent deux
processus distincts partageant le même fichier exécutable mais des données
différentes.
Les ordinateurs capables d’exécuter réellement plusieurs instructions dans
plusieurs processeurs sont des machines à architecture dite parallèle, dotées
d’autant de processeurs que de calculs simultanés. On trouve dans le commerce
de telles machines, puissantes mais onéreuses, inadaptées pour un utilisateur
qui n’a pas besoin d’une grande puissance de calcul mais qui ne veut pas être
bloqué durant une compilation.
Les systèmes d’exploitation multitâches ont été conçus pour ce genre d’uti-
lisation. Ils gèrent un partage de temps du processeur et donnent aux utilisa-
teurs l’impression de disposer d’autant de processeurs (virtuels) que de travaux
à mener de front. Les contraintes matérielles sur l’ordinateur se résument es-
sentiellement à une taille de mémoire suffisante pour contenir l’ensemble des
programmes à exécuter et à l’existence d’un mécanisme d’interruption.
La présentation du mécanisme de partage de temps dans ce paragraphe et
les paragraphes 2. et 3. suppose que les programmes ont déjà été lancés et que
les processus préexistent.
élection
a3
a2
a1
IRQ
t
di1 ri1 di2 ri2 di3 ri3 di4 ri4 di5 ri5 di6
l’exécution de l’activité a2. Cette dernière est à son tour suspendue par le
deuxième départ en interruption (di2 ) et sera reprise lors du quatrième retour
d’interruption (ri4 ).
L’intervalle de temps entre les deuxième et troisième interruptions est utilisé
par a3, dont l’exécution sera reprise puis suspendue à nouveau aux instants
respectifs ri5 et di6 .
Le même scénario se reproduit toutes les trois interruptions et le processeur
travaille à tour de rôle pour la première activité, la deuxième, et ainsi de suite
jusqu’à la dernière, après quoi le processeur est réaffecté à la première activité
et cette séquence se reproduit à l’identique dans un nouveau cycle.
Le processus associé à l’activité en cours d’exécution est le processus actif
ou élu. Les autres processus sont dits prêts (sous entendu à poursuivre leur
exécution lorsque le processeur leur sera attribué de nouveau). Dans le para-
graphe 3. du chapitre 24 nous verrons que les processus peuvent aussi se bloquer
en attente d’événements tels que des fins d’entrées/sorties par exemple.
On parle de commutation d’activité, ou de commutation de processus, pour
désigner le passage d’une activité, et évidemment d’un processus, à une autre.
La figure 23.2 en détaille le déroulement. Cette commutation est déclenchée
par une interruption. Le traitant est alors appelé traitant de commutation.
Lors du retour d’interruption, une activité doit retrouver la machine dans
l’état où elle l’a laissée lors de sa suspension. L’état de la machine est sau-
vegardé dans des structures de données que nous étudions en détail au para-
graphe 2..
La première étape de la commutation (s) est représentée en traits fins sur
les figures 23.1 et 23.2. Elle sauvegarde le contexte (essentiellement le contenu
des registres du processeur) de l’activité suspendue (a2) dans les structures de
données du processus actif (p2).
Le mécanisme de départ en interruption intégré au processeur sauvegarde
automatiquement certains registres. La sauvegarde des autres registres est ex-
plicitement programmée dans le prologue du traitant de commutation.
Le déroulement de l’interruption se termine par une séquence symétrique de
534 Partage de temps et processus
ordonnanceur
r
activité a3
RTI
s RTI
activité a2
processus
p2 néant p3
actif
exécution traitant
IRQ
restauration (r) du contexte du (nouveau) processus actif (p3) depuis les struc-
tures de données convenables. Une partie de cette séquence est programmée
dans l’épilogue du traitant, et l’autre réalisée automatiquement par l’instruc-
tion rti. On a évidemment symétrie par rapport au départ.
Le système d’exploitation comporte des procédures chargées d’élire un nou-
veau processus actif. Ces procédures constituent l’ordonnanceur. Le corps du
traitant appelle les procédures de l’ordonnanceur.
L’exécution des programmes des utilisateurs, l’activité, cesse dès le départ
en interruption et ne reprend qu’au retour d’interruption. Mais on notera que
du point de vue de la gestion des structures de données du système, la com-
mutation effective de processus a lieu entre la fin de la sauvegarde et le début
de la restauration, qui utilisent les structures de données de l’ancien (p2) et
du nouveau (p3) processus actifs.
L’ordonnanceur n’est pas une activité comme une autre. Il ne résulte pas
du lancement d’une commande et le système ne maintient pas de structures
de données pour lui ; ainsi, l’ordonnanceur n’est pas un processus.
On note une différence par rapport à l’exécution d’un rti normal : ici l’acti-
vité reprise n’est pas nécessairement celle qui a été interrompue lors du départ
en interruption. L’ordonnanceur peut choisir et installer une autre activité.
est doté à cet effet d’une zone de mémoire privée destinée à la sauvegarde et
à la restauration du contenu des registres, y compris le pointeur de pile SP
(dans le prologue et l’épilogue du traitant de commutation).
Enfin il est nécessaire que la suspension n’ait pas d’incidence directe sur les
entrées et sorties effectuées par le processus suspendu. Il suffit pour cela de do-
ter les processus de tables de fichiers ouverts individuelles et de terminaux de
dialogue distincts. Les systèmes multi-utilisateurs sont évidement dotés d’au-
tant d’écrans-claviers que de personnes utilisant simultanément la machine.
Mais lorsque les processus sont lancés par le même individu depuis le même
écran-clavier, l’écran peut être divisé en fenêtres qui constituent autant de
terminaux virtuels.
Quand un processus est actif, les registres du processeur (Rétat, PC, SP,
registres données) contiennent des valeurs à jour. Quand le processus est inac-
tif, les informations pertinentes sont en mémoire dans la zone de sauvegarde
du processus.
On peut ainsi assimiler un processus à une machine virtuelle exécutant
un programme, dont la mémoire est consituée des zones texte, données
et pile allouées au processus, et dont le processeur est représenté par la
zone de sauvegarde des registres en mémoire.
suivant
proc
état
0
texte
1 T début
table des
T fin
PID proc[PID]
données
TPROC D début
processus D fin
n-1 pile
P début
actif P fin sregs
tête-prêts ssp
queue-prêts
Fig. 23.3 – Représentation d’un processus en mémoire : à gauche les variables globales
du système dont la table des processus, au centre le détail d’un descripteur
de processus et à droite les zones de mémoires affectées à un processus
(texte, données et pile du processus). La portion de zone pile déjà utilisée
est représentée en gris plus foncé.
processus : actif, prêt ou inexistant (pour les entrées de la table encore inoc-
cupées). Le descripteur contient aussi les adresses de début et de fin des zones
texte (T), données (D) et pile (P) allouées au processus. Le champ ssp contient
une copie du registre pointeur de pile SP.
pid retirer_pr^et () {
pid élu;
élu = t^
ete_pr^
ets;
tete_pr^ets = proc[tete_pr^
ets].suivant;
return (élu); }
540 Partage de temps et processus
traitant :
prologue :INITdesc (R1, actif) /* actif est Pprec */
st SP, [R1+deltassp] /* proc[Pprec].ssp = SP */
pret
s R1
r
Pdébut
e Pfin
g Rn-1 ssp
REtat
R0 = 0 s
PC
R1
descripteur
Pnouv
Pile Pnouv
Rn-1 pret
REtat
PC sregs
SP
Registres du Pdébut
processeur Pfin
ssp
Fig. 23.5 – Contenu de la mémoire et des registres du processeur lors d’une commuta-
tion du processus Pprec au processus Pnouv, au point d’observation avant
l’exécution de l’instruction rti
3. Organisation du traitant de commutation 543
traitant :
/* les registres ordinaires ont été empilés par le processeur
dans la pile de Pprec */
restaurer :
pointobsB : /* Point d’observation */
INITdesc (R1, actif) /* actif est Pnouv */
ld [R1+deltassp], SP /* SP = proc[Pnouv].ssp */
pointobsC : /* Point d’observation */
/* Corrige l’adresse de retour pour exécution
du rts de la procédure restaurer */
set reprise, R1
st R1, [SP] /* sommet de pile = l’adresse reprise */
pointobsD : /* Point d’observation */
rts /* retour à reprise */
(a) (b)
descripteur descripteur
Pprec Pprec
pile Pprec pile Pprec
election election
sregs sregs
SP
(c) (d)
descripteur descripteur
Pprec Pprec
pile Pprec pile Pprec
election election
sregs sregs
descripteur descripteur
Pnouv pile Pnouv Pnouv pile Pnouv
election reprise
sregs SP sregs SP
4.1 Création
La procédure de création retourne au processus père appelant le pid du fils
créé, ou un code d’erreur (manque de mémoire ou d’entrée libre dans la table
des processus ; fichier inexistant ou ne contenant pas un binaire exécutable
valide pour la machine, ou droits du père insuffisants). Elle possède un sur-
ensemble des paramètres du chargeur-lanceur (excepté le début de mémoire
libre) auquel elle se subtitue. Les paramètres supplémentaires sont relatifs à
la gestion des droits d’accès du processus créé. Par défaut, le processus fils
appartient au même utilisateur que le processus père : seuls les processus ap-
partenant au superutilisateur sont autorisés à créer des fils appartenant à un
autre utilisateur.
La procédure de création alloue au fils une entrée libre pid fils de la table
des processus ainsi qu’une pile système si elle n’est pas incluse dans pid fils.
Comme dans le système simple, l’en-tête du fichier exécutable est lue et trois
zones de mémoire texte, données et pile utilisateur sont allouées au processus
créé. Le contenu des sections texte et données du fichier exécutable est chargé
dans la mémoire allouée au processus ; l’algorithme de réimplantation est alors
appliqué.
La procédure de création reproduit les paramètres passés par le père dans
la pile utilisateur du fils et initialise le champ sommet de pile système (ssp) de
pid fils ainsi que la zone de sauvegarde pointée par ce dernier.
Le fils est ensuite inséré dans l’arborescence de filiation des processus et
dans la liste des processus prêts.
Noter que l’initialisation de la zone de sauvegarde dans la pile système
simule un processus préexistant interrompu par une interruption de commu-
tation et remis dans la file des prêts.
4.2 Terminaison
La procédure d’autodestruction a pour paramètre un code de retour à trans-
mettre au processus père, indiquant soit une exécution correcte, soit l’erreur
ayant causé la terminaison prématurée du fils. Ce paramètre de l’appel de
terminaison est recopié dans l’entrée de la table des processus correspondant
au processus exécutant l’appel (donc le processus actif), où le père pourra le
consulter ultérieurement.
Les zones texte, données et pile processus sont désallouées et rajoutées à
l’espace mémoire disponible.
Dans la table des processus, la routine de destruction réinitialise le champ
père de chacun de ses processus fils, qui deviennent orphelins, et purge les fils
fantômes de la table des processus (les entrées correspondantes de la table des
processus sont remises dans la liste des entrées libres).
Le processus en cours d’autodestruction devient un processus fantôme ou
zombie (mort-vivant) qui n’évolue plus et qui n’utilise plus d’autre ressources
548 Partage de temps et processus
5. Exercices
E23.1 : Sauvegarde partielle ou totale des registres
Généralisation du mécanisme
d’interruption et applications
Donnée invalide Il existe des machines où une interruption est déclenchée
en cas d’apparition d’une instruction de division avec un opérande diviseur
nul. Une autre situation d’interruption se produit si il y a accès à un mot
alors que l’adresse n’est pas multiple de 4 dans une machine où une telle
convention existe. Sur certaines machines des instructions particulières peuvent
faire déborder certaines ressources. C’est le cas sur le sparc où les instructions
save ou restore peuvent provoquer des interruptions pour débordements de
fenêtre.
PC ⇐= PC+4 TA ⇐= PC
RInst ⇐= M[TA]
Load
swi autres
reg ⇐= M[TA] source ⇐= 3
TA,SP ⇐= SP-4
M[TA] ⇐= PC
TA,SP ⇐= SP-4
M[TA] ⇐= REtat
REtat[I] ⇐= Vrai
TA ⇐= f(source)
TA ⇐= M[TA]
PC ⇐= M[TA]
Fig. 24.1 – Graphe de contrôle (très partiel) du processeur acceptant plusieurs sources
d’interruptions
du mode d’exécution. L’appel de procédure, qui fait usage de SP, est ainsi
indépendant du mode dans lequel s’exécutent les procédures.
z-1 z t
fZ b z z+1
a≤x≤b
x z-1 z d
z+1
dZ a
z-1 z p
z+1 z+1
Fig. 24.3 – Comparateur simple entre une adresse et des débuts (dZ) et fin (fZ) de zones.
Les trois comparateurs sont cascadés. L’entrée S est le bit superviseur. En
effet la vérification est faite en mode Utilisateur seulement.
Rtrad
Super/Util
+
Adresse logique 0 Adresse physique
Les états des processus Aux deux états Prêt et Actif des processus, nous
ajoutons un état Endormi (ou Bloqué). Ces 3 états sont représentés sur la
figure 24.5. Détaillons les raisons qui font passer un processus d’un état à un
autre :
1. Un processus Actif qui n’a pas d’entrées/sorties en cours peut être mis
sur la file des Prêts à la suite d’une interruption causée par la pendule
périodique de l’ordinateur.
2. Un processus Prêt, en tête de file, peut être remis en état Actif à la suite
d’une interruption causée par la pendule périodique de l’ordinateur.
Ces deux situations ne sont pas nouvelles par rapport au chapitre 23.
3. Si un processus Actif a besoin d’une action d’entrée/sortie, il fait un appel
système (par une instruction swi) en donnant toutes les informations
sur la nature de l’entrée/sortie souhaitée. Le système d’exloitation lance
l’action en écrivant dans le coupleur les commandes nécessaires et met
le processus en état Endormi.
4. Un processus Endormi sur attente du résultat d’une certaine action
d’entrée/sortie est remis dans la file des Prêts par le gestionnaire de
processus dès que l’interruption de fin d’entrée/sortie est émise par le
coupleur en question.
La figure 24.6 donne deux situations d’entrelacement de quatre processus.
Dans la situation a, seul le processus P2 lance une action d’entrée/sortie et
l’action est faite rapidement par rapport à la période de la pendule. Dans la
situation b, P2 et P3 lancent des actions et le traitement de l’entrée/sortie de
P2 est un peu plus long.
3. Entrées/sorties gérées par interruption 567
a)
SWI IRQfinE/S
Période de pendule
P1
b) P2 endormi
P3 endormi
P4
attendre périphérique () {
dormeur = actif ;
proc[actif].endormir = VRAI ;
/* autoriser IT au niveau du coupleur */
*commande coupleur = AUTORISER IT ;
appel superviseur dormir ; }
traitant dormir () {
empiler () ;
sauver () ;
masquer it () ;
/* la manipulation de la structure décrivant les processus */
/* nécessite une exclusion mutuelle */
si proc[actif].endormir alors actif vers bloqué () ;
démasquer it () ;
restaurer () ;
dépiler () ; }
actif vers bloqué () {
proc[actif].état = BLOQUE ;
pr^et vers actif () ; }
traitant reveil () {
empiler () ;
masquer it () ;
/* la manipulation de la structure décrivant les processus */
/* nécessite une exclusion mutuelle */
si proc[actif].état == BLOQUE ;
alors
bloqué vers actif () ;
sinon
/* le processus n’a pas eu le temps de s’endormir */
proc[actif].endormir = FAUX ;
démasquer it () ;
dépiler () }
bloqué vers actif () {
proc[dormeur].état = PRET ;
ajouter pr^et (dormeur) ; }
valeur, 6
absolue, 58
passage par , 323
variable, 7, 76
booléenne, 28, 224
d’état, 225
locale, 81
temporaire, 334
vecteur de bits, 7, 50, 143
verrou, 192, 195, 384
vie des programmes, 93, 435
Bibliographie
[Int97] Intel : The complete guide to MMX technology. McGraw-Hill, 1997. 1.4.1
[Kar53] M. Karnaugh : The map method for synthesis of combinational logic. Circuits
AIEE Transactions on Communications and Electronics, pages 593–599, 1953.
4.
[KB90] R.E. Bryant K. Brace, R. Rudell : Efficient implementation of a bdd package.
Proceedings of the 27th ACM/IEEE Design Automation Conference IEEE0738,
1990. 4.3
[Kra85] S. Krakowiak : Principes des systèmes d’exploitation des ordinateurs. Dunod
Informatique, 1985. 4.2, 4.
[Kun65] Jean Kuntzmann : Algèbre de Boole. Dunod, 1965. 4.
[Kun67] Jean Kuntzmann : Sélection parmi des communications présentées au colloque
consacré à l’Algèbre de Boole. Dunod, 1967. 4.
[Las98] J. Lassègue : Turing. Les belles lettres - collection Figures du savoir, 1998.
2.1
[Liv78] C. Livercy : Théorie des programmes - schémas, preuves, sémantique. Dunod,
1978. 2.
[Mul89] J.M. Muller : Arithmétique des ordinateurs. opérateurs et fonctions
élémentaires. Masson, collection Etudes et Recherches en Informatique, 1989.
2.2
[Org72] J.E. Organick : The MULTICS system : an examination of its structure. The
MIT Press, Cambridge MA, 1972. 2.6
[SFLM93] P.-C. Scholl, M.-C. Fauvet, F. Lagnier et F. Maraninchi : Cours d’in-
formatique : langages et programmation. Masson, 1993. 1., 1.4, 2.1, E8.9
[SG96] A. Silberschatz et P. B. Galvin : Principes des systèmes d’exploitation.
Addison-Wesley, 1996. 4.
[SL96] André Seznec et Fabien Lloansi : Etude des architectures des microproces-
seurs mips r10000, ultrasparc et pentiumpro. Publication interne 1024, INRIA,
Programme 1 - Architecture parallèles, bases de données, réseaux et systèmes
distribués – Projet CAPS, mai 1996. 1.4.4
[Tan94] A. Tanenbaum : Systèmes d’exploitation : systèmes centralisés, systèmes dis-
tribués. InterEditions, 1994. 4.
[Tur54] A. M. Turing : Solvable and unsolvable problems. Science News, 31:7–23,
1954. 2.1
[WM94] R. Wilhelm et D. Maurer : Les compilateurs : théorie, construction,
génération. Masson, 1994. 1.1.1, 2.2.1, 1.2
[Zak80] Rodnay Zaks : Applications du 6502. Sybex, 1980. 2.1.1