Vous êtes sur la page 1sur 50

CSC4536– Compilation : De

l'algorithme à la porte logique


Portail informatique

 Organisation
o
o
o
o

 Mementos et Manuels
o
o
o
o
o
o
 Archives tar
o
o

 Supports
o




o



o



o




o


o

o

o

o

o

o

o


o



o


o



o



o

Du transistor au processeur
Ce Cours/TP a pour objectif de vous faire comprendre comment est conçu un
processeur à partir de transistors.Il est probable que de terminer ce TP vous
demande environ 2 à 3h de travail en hors-présentiel (exercices 8 et 9). Finir ces
exercices n'est pas nécessaire pour la suite du cours, mais réussir à finir est
relativement satisfaisant puisque vous aurez réussi à concevoir un processeur 8 bits
complet relativement avancé.

De transistor aux portes logiques (∼ 40mn)

Dans ce cours, on suppose que nous sommes sur une île déserte et que nous
devons reconstruire des ordinateurs à partir de zéro. Heureusement, nous avons un
ami physicien sur l'île. Cet ami est capable d'assembler des circuits complexes
construits à partir de transistors, l'élément de base de la plupart des circuits
électroniques. Pour reconstruire un ordinateur, on considère des transistors de type
MOS (Metal-Oxide-Semiconductor) car ce sont ceux aujourd'hui les plus
couramment utilisés pour construire les micro-processeurs. Il existe deux types de
transistors MOS : les transistors nMOS et les transistors pMOS. Dans les
processeurs actuels, on trouve les deux types de transistors, on parle donc de
technologie CMOS (Complementary Metal-Oxide-Semiconductor).

Transistors nMOS et cMOS.


Comme présenté sur la figure , un transistor de type MOS possède trois connexions :

 la source, nommée S,
 la grille, nommé G.
 le drain, nommé D,

Un transistor MOS se comporte comme un interrupteur électrique. Pour un nMOS,


lorsque la grille est alimentée, la source est connectée au drain, alors que pour un
transistor pMOS, c'est l'inverse. Pour illustrer ce fonctionnement, si on imagine une
rivière coulant de la source vers le drain, la grille agit comme une sorte de barrage
empêchant l'eau de passer. Dans le cas d'un nMOS, lorsque la grille est alimentée,
c'est comme si le barrage était ouvert : le courant passe de la source au drain. Dans
le cas d'un pMOS, c'est l'inverse : lorsque la grille est alimentée, le barrage est fermé
et le courant ne peut pas passer.Du transistor à la porte NAND
 Les transistors permettent de construire des portes logiques qui sont à la base de
l'électronique. À cette question, nous étudions notre première porte logique : la porte
NAND (pour Not AND). L'avantage de la porte NAND, c'est que c'est une porte
logique universelle : elle permet de construire des portes logiques NOT, OR, AND ou
XOR comme vous le verrez dans l'exercice suivant.

Porte NAND

A B Out
0 0 1
0 1 1
1 0 1
1 1 0
.bTable de vérité de la porte logique
.aLa porte logique NAND (credit wikipedia).
NAND.
La construction de la porte logique NAND à partir de portes nMOS et pMOS est
donnée en figure .a, alors que la table de vérité de cette porte est donnée en
figure .b. Dans la figure .a, Vdd est le courant haut qui correspond, par convention, à
la valeur vrai. Vss est le courant bas qui correspond, par convention, à la valeur faux.
Les deux transistors du haut (avec un petit rond sur la grille) sont des pMOS qui
empêchent le courant de passer entre la source et le drain lorsque la grille est
alimentée, alors que les deux transistors du bas (sans petit rond sur la grille) sont
des nMOS qui laissent donc le courant passer entre la source et le drain lorsque la
grille est alimentée.
Vérifiez que le circuit présenté en figure .a met bien en œuvre une porte NAND.Prise
en main du simulateur de circuit

Connexion

 Maintenant que vous avons notre première porte logique, nous pouvons l'utiliser
pour construire les autres portes logiques. Avant de nous lancer dans cette mise en
œuvre, nous prenons en main l'environnement de développement que nous
utiliserons pour créer des circuits.
Dans un navigateur Web, connectez-vous au site https://circuitverse.org/simulator.
Ce site permet de construire des circuits logiques de façon conviviale. De façon à
pouvoir enregistrer vos travaux, il faut créer un compte. Pour créer un compte,
cliquez sur 
Sign In
. Ensuite, cliquez sur 
New user? Sign up
. À partir de ce point, vous pouvez soit créer un nouveau compte, soit, si vous en
avez un, utiliser votre compte 
google
 ou votre compte 
facebook
 pour vous authentifier.

Configuration d'un projet

 Maintenant que vous avez un compte, vous pouvez entrer dans le simulator. Cliquez
sur le menu 
Simulator
 qui se trouve en haut de la fenêtre. Vous pouvez commencer à configurer votre
projet. Donnez lui un nom, par exemple 
CSC4536
, en modifiant la partie 
Project Properties
 se trouvant en bas à gauche de la fenêtre.

Les entrées

 Avant d'aller plus loin, nous allons placer un premier élément dans notre circuit.
Allez dans l'onglet 
Input
 se trouvant à gauche de la fenêtre et sélectionnez la première icône (lorsque vous
passez dessus avec la souris, vous devriez voir 
Input
). Déplacez votre souris sur l'espace de travail (la grille se trouvant à droite de la
fenêtre), et cliquez quelque part. Félicitations, vous avez construit une 
entrée binaire
 ! Vous devriez constatez que, lorsque vous cliquez sur votre entrée binaire, vous
pouvez la faire passer de 0 à 1 et inversement.

Enregistrement

 Avant d'aller plus loin, nous allons vérifier que nous pouvons bien sauver notre
travail. Dans le menu 
Project
, cliquez sur 
Save Online
, ce qui va créer une sauvegarde de votre projet hébergée sur le serveur (vous
pouvez aussi enregistrer votre travail hors-ligne avec 
Save Offline
). Pour vérifier que tout s'est bien passé, allez dans la configuration de votre compte
représenté par votre nom d'utilisateur en haut à droite de la fenêtre, et cliquez sur 
My circuits
. Vérifiez que vous retrouver bien votre projet et cliquez sur 
Launch
 pour relancer le simulateur.

Déplacer un élément

 Avant de se lancer dans des conceptions plus intéressantes, nous allons juste
apprendre à créer un circuit simple constitué d'une unique porte logique NAND. Vous
trouverez cet élément dans l'onglet 
Gates
 à gauche de la fenêtre. Dans un premier temps, ajoutez une seconde entrée et une
porte logique NAND. Vous devriez remarquer que, lorsque vous passez sur un
élément dans l'onglet Gates, son nom s'affiche. Vous devriez aussi remarquer que,
lorsque vous cliquez sur un élément placé dans l'espace de travail, vous pouvez le
déplacer.Pour déplacer un élément, il faut cliquer à l'intérieur de l'élément et non sur
les petits points verts se trouvant sur le bord et représentant les points de
connexions.

Constructions de fils (1/2)

 Nous allons maintenant apprendre à connecter les deux entrées à la porte logique.
Pour connecter deux éléments, il faut créer un fil électrique entre ces éléments. Les
points de connexions sont représentés par les points verts se trouvant sur le bord
d'un élément. Lorsque vous cliquez sur un point de connexion et que vous déplacez
votre souris, vous commencez à créer un fil. Connectez les entrées à la porte NAND.

Sorties
 Pour finir, nous allons créer une sortie permettant de visualiser le résultat du calcul
effectué par notre porte NAND. Dans l'onglet 
Output
 à gauche de l'écran, sélectionnez le premier élément (nommé 
Output
). Cet élément, que nous appellerons une sortie binaire, permet d'afficher l'état d'un
fil électrique (0 ou 1). Ajoutez une sortie binaire et connectez la sortie de la porte
NAND à cette sortie. Vous devriez obtenir le circuit présenté en figure . Vérifiez, en
modifiant les entrées, que votre circuit respecte bien la table de vérité de la porte
NAND. Vous devriez remarquer qu'un fil alimenté (valeur 1) apparaît en vert clair
alors qu'un fil non alimenté (valeur 0) apparaît en vert foncé.

Un circuit NAND.

Constructions de fils (2/2)

 Pour continuer cette prise en main de l'outil, nous allons apprendre à mettre un
circuit en court circuit, c'est-à-dire que nous allons à la fois mettre les valeurs 0 et 1
sur un fil. Il ne faut bien sûr jamais mettre un circuit en court-circuit, cette question n'a
que pour but de vous montrer ce qui se passe, ce qui va vous permettre d'identifier
les erreurs dans vos circuits.
Pour réaliser ce court circuit, cliquer quelque part sur le fil reliant une des entrées à
la porte NAND, ce qui permet de démarrer un second fil à partir de ce point.
Construisiez un fil qui contourne entièrement la porte (par le bas si vous partez du
bas ou par le haut sinon). Pour cela, il faut construire plusieurs fils. Vous devriez
obtenir le résultat présenté en figure . Vous devriez voir des messages s'afficher en
bas de la fenêtre vous indiquant qu'il y a un problème. Vous devriez aussi voir que le
fil reliant la porte à la sortie est à moitié vert foncé et à moitié vert clair, et que le
simulator vous a ajouté de petits rond aux points de connexions qui permettent de
remonter à l'origine du problème (sur le haut de la figure dans le cas de la figure ).

Un circuit NAND en court-circuit.

Suppression d'éléments

 Nous allons réparer notre circuit en détruisant le court-circuit. Pour cela, cliquez sur
un fil et appuyez sur la touche 
backspace
 (retour arrière se trouvant en haut à droite de votre clavier), ce qui détruit un
élément. Supprimez tous les fils réalisant le court-circuit.

Déplacer plusieurs éléments

 Pour finir notre prise en main, nous apprenons à déplacer plusieurs éléments. Pour
cela, il suffit d'effectuer une sélection en appuyant sur la touche 
Shift
 (remarquez que si vous n'appuyez pas sur la touche 
Shift
 alors que vous cliquez sur une zone sans éléments avant de déplacer la souris,
vous déplacez simplement la grille). Ensuite, vous pouvez, en cliquant sur l'un des
éléments sélectionnés , déplacer l'ensemble de ces éléments.De la porte NAND aux
autres portes logiques
 Le but de cet exercice est de construire les portes logiques manquantes à partir de
la porte logique NAND. Les tables de vérités et les icônes associées aux portes que
vous devez construire sont présentées en figure .
Porte OR
Porte Porte Porte Porte
NOT AND NOR XOR
Porte
XNOR

O O O O O
a b a b a b a b a b
ut ut ut ut ut

Ou 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1
a
t 0 1 0 0 1 1 0 1 0 0 1 1 0 1 0
0 1 1 0 0 1 0 1 1 0 0 1 0 1 1 0 0
1 0 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1

Tables de vérités des principales portes.


Commencez par nettoyer votre espace de travail en supprimant le circuit créé à
l'exercice précédent. Pour cela, sélectionnez tout votre circuit et utilisez 
backspace
 pour le supprimer (vous pouvez aussi aller dans le menu 
Project->Clear Project
).
Ensuite, en considérant que vous n'avez que la porte logique NAND, mettez en
œuvre les autres portes. Au fur et à mesure que vous construisez de nouvelles
portes, vous pouvez bien sûr les utiliser pour construire les suivantes.
Félicitations, vous avez maintenant construit les éléments de base permettant de
concevoir des ordinateurs à partir de transistors !

L'additionneur (∼ 80mn)

À cette question, nous réalisons un circuit permettant d'additionner deux nombres


encodés sur 8 bits.L'additionneur 1 bit

Le demi-additionneur 1 bit

 Comme première étape, nous allons réaliser un circuit permettant d'additionner deux
bits (un bit est une valeur binaire, pouvant donc prendre les valeurs 0 ou 1).
Exactement comme on le fait en décimal, il peut il y avoir une retenue (carry en
anglais), donc votre circuit doit produire un résultats sur deux bits :

 0 + 0 donne 0 et je retiens 0,
 0 + 1 donne 1 et je retiens 0,
 1 + 0 donne 1 et je retiens 0,
 1 + 1 donne 0 et je retiens 1 (comme 5 + 5 donne 0 et je retiens 1 en décimal),

a b s cout
0 0 0 0
0 1 1 0
1 0 1 0
1 1 0 1
Table de vérité d'un demi-additionneur.
La figure  vous donne la table de vérité de l'additionneur. Les entrées 
a
 et 
b
 sont les nombres à additionner alors que 
s
 est la somme et 
cout
 la retenue.
Commencer par supprimez tous vos anciens circuits. À partir de cette étape, ne
détruisez plus jamais aucun des circuits que nous aurons réalisés : il vont tous nous
resservir à un moment où un autre.
Ensuite, réalisez le circuit permettant d'effectuer une addition binaire.
Réutilisation d'un circuit

 De façon à additionner plusieurs bits, nous aurons besoin de réaliser plusieurs fois
le circuit d'addition d'un unique bit. Notre logiciel de simulation permet de réutiliser
des circuits qui ont déjà été mise en œuvre.
Pour rendre votre circuit réutilisable, commencez par donner un nom à vos entrées
et à vos sorties. Pour cela, sélectionnez une des entrées. Dans la partie gauche de
la fenêtre, vous devriez voir apparaître une zone 
Properties
. À la ligne 
Label
, donnez un nom à vos deux entrées (
a
 et 
b
). De la même façon, donnez des noms à vos deux sorties (
s
 pour la somme et 
cout
 pour la retenue).
Ensuite, vous pouvez donner un nom à votre circuit. Pour cela, cliquez n'importe où
sur la grille en dehors de tout élément. Ensuite, dans 
Project Properties
, remplacez le nom actuel du circuit (ligne 
Circuit
 indiquant 
Main
) en 
half-add
. Le nom que vous voyez au dessus de la grille devrait changer de façon adéquate.
Enfin, cliquez sur 
Edit layout
 en bas à gauche de la fenêtre, ce qui vous permet de modifier l'apparence de votre
circuit quand vous le réutilisez. Nous vous conseillons de laisser les entrées à
gauche et les sorties à droite. Nous vous conseillons aussi de mettre 
a
 en haut et 
b
 en dessous, et 
s
 en haut et 
cout
 en haut bas. Enfin, nous vous conseillons de laisser deux carreaux entre les entrées
ou entre les sorties. Après avoir donné l'apparence qui vous plaît à votre circuit,
appuyez sur 
Save
 pour revenir sur le schéma du circuit. Pensez à sauver votre travail (menu 
Project->Save Online
).
L'additionneur 1 bit

 Comme on le fait en décimal, quand on effectue un calcul sur plusieurs digits, il faut
non seulement additionner les deux digits courants, mais il faut aussi additionner ce
résultat avec la retenue de l'étape précédente. Nous réalisons donc ce circuit à cette
étape. Créez un nouveau circuit (menu 
Circuit->New circuit
) que vous nommerez 
1-add
 (pour additionneur 1 bit).
Ajoutez 3 entrées nommées 
a

b
 et 
cin
 (retenue d'entrée provenant de l'étape précédente). Ajoutez aussi 2 sorties
nommées 
s
 pour la somme et 
cout
 pour la retenue de sortie.
Enfin, utilisez des circuits 
half-add
 (menu 
Circuit->Insert SubCircuit
) pour que 
s
 soit égal à la somme de 
a

b
 et 
cin
, et que 
cout
 soit égal à la retenue de cette somme. Du bit à l'entier

L'entrée 8 bits

 De façon à pouvoir créer un additionneur capable d'effectuer des additions sur des
nombres encodés sur 8 bits, nous commençons par créer une entréee 8 bits. Créez
un nouveau circuit nommé 
8-add
. Dans ce circuit, ajoutez une entrée. Si vous sélectionnez cette entrée, vous devriez
voir apparaître ses propriétés. Donnez la valeur 8 à la propriété 
BitWidth
 de façon à ce que l'entrée soit une entrée avec 8 bits. Donnez le nom 
a
 à votre entrée.Si vous n'êtes pas à l'aise avec la représentation des nombres en
binaire et en hexadécimal, cliquez sur l'aide.

Affichage d'un entier 8 bits

 Le simulateur 
circuitverse
 permet d'afficher un nombre 4 bits facilement. Pour utiliser cette fonctionnalité, nous
avons donc besoin de séparer notre nombre 8 bits en deux nombres sur 4 bits, l'un
avec les 4 bits de poids fort, et l'autre avec les 4 bits de poids faibles. Dans l'onglet 
Misc
, sélectionnez un 
Splitter
 (deuxième icône). Quand 
circuitverse
 vous demande la taille du nombre, mettez 8 puisque votre nombre fait 8 bits.
Ensuite, il faut donnez la décomposition de notre nombre. Nous voulons deux
groupes de 4 bits, donc saisissez 4 4, ce qui construit 2 sorties de 4 bits. Connectez
le 
Splitter
 à 
a
. Ensuite, dans l'onglet 
Output
, sélectionnez 
HexDisplay
 (5ième icône) et placez deux 
HexDisplay
 à côté de votre 
Splitter
. Connectez les 4 premiers bits du 
Splitter
 au second 
HexDisplay
 et les 4 derniers bits au premier 
HexDisplay
. Vous devriez obtenir le circuit présenté en figure .

Décomposition et affichage d'un nombre


8 bits.L'additionneur 8 bits
 Vous pouvez maintenant créer l'additionneur 8 bits. Pour cela, vous avez besoin des
entrées suivantes :
 a

 sur 8 bits que vous avez déjà créé à la question précédente,


 b

 sur 8 bits que vous devez ajouter. Pensez à ajouter des 

HexDisplay

 et un 

Splitter

 pour voir la valeur de 

 cin

 sur 1 bit que vous devez ajouter. Cette entrée donne la retenue initiale, qui
est bien sûr égale à 0 pour une addition, mais vous verrez dans les questions
suivantes comment exploiter cette entrée pour faire des soustractions.

Ensuite, utilisez des 


Splitter
 8 vers 1 1 1 1 1 1 1 1 pour retrouver les bits qui composent 
a
 et 
b
. Ajoutez aussi 8 circuits 
1-add
 et connectez les de façon adéquate pour créer un additionneur 8 bits.
Pour construire la sortie, ajoutez un 
Splitter
 8 vers 1 1 1 1 1 1 1 1 et changez sa direction vers LEFT dans ses propriétés.
Connectez les sorties des additionneurs 1 bits au 
Splitter
, et connectez de l'autre côté du 
Splitter
 une sortie nommée 
s
 dans laquelle vous spécifierez que 
BitWidth
 est égal à 8. Ajoutez aussi des 
HexDisplay
 et un 
Splitter
 8 vers 4 4 pour voir le résultat. Enfin, ajoutez une sortie 
cout
 à votre circuit.
Si gérer autant de fils vous semble trop difficile, n'hésitez pas à créer d'abord un
circuit 4-add pour additionner des entiers 4 bits et à utiliser ce circuit pour construire
le 8-add.
Félicitations, vous venez de créer votre premier composant électronique complexe !

Le multiplexeur (∼ 30mn)

Dans un circuit, il arrive souvent d'avoir besoin de sélectionner une entrée parmi N
en fonction d'une valeur. Ce mécanisme peut par exemple être utilisé pour
sélectionner les registres qui participent à une instruction. Dans cet exercice, nous
mettons un œuvre des multiplexeurs dont le rôle est justement de sélectionner une
entrée parmi N.

2x1-mux

 On commence par construire un multiplexeur permettant de sélectionner une entrée


parmi deux et dans lequel chaque entrée est un bit. Créez un nouveau circuit
nommé 
2x1-mux
. Ce circuit doit posséder deux entrées binaires nommées 
x0
 et 
x1
, et un sélecteur nommé 
sel
. Le circuit doit aussi posséder une sortie binaire nommée 
out
. Quand 
sel
 vaut 0, le circuit doit renvoyer 
x0
 sur 
out
, et quand 
sel
 vaut 1, le circuit doit renvoyer 
x1
 sur 
out
.

2x8-mux

 Sur le modèle de l'additionneur 8 bits, construisez un nouveau circuit nommé 


2x8-mux
 prenant en entrée 2 valeurs sur 8 bits nommées x0 et x1, et un sélecteur sur un bit.
Le circuit doit renvoyer x0 si sel vaut 0 et x1 sinon.

2x3-mux
 Sur le modèle de 
2x8-mux
, construisez un nouveau circuit nommé 
2x3-mux
 prenant en entrée 2 valeurs sur 3 bits nommées x0 et x1, et un sélecteur sur un bit.
Le circuit doit renvoyer x0 si sel vaut 0 et x1 sinon.Travailler sur des entiers encodés
sur 3 bits peut vous sembler inattendu. Il faut comprendre qu'avec 3 bits, on peut
encoder 23 = 8 valeurs. Comme à terme, notre processeur possédera 8 registres,
nous aurons donc besoin de manipuler des numéros de registre encodés sur 3 bits.
Le 
2x3-mux
 permettra donc de sélectionner des numéros de registres.

8x8-mux

 À partir de 
2x8-mux
, construisez un circuit 
8x8-mux
 permettant de sélectionner une entrée parmi 8, chacune des entrées faisant 8 bits.

L'unité de calcul arithmétique et logique (∼ 30mn + ∼ 90mn en


optionnel)

Dans cet exercice, nous concevons un circuit nommé 


ALU
 permettant de réaliser n'importe quel calcul arithmétique et logique. Vu le temps
imparti, nous ne pourrons pas être exhaustifs, mais notre ALU va être capable
d'effectuer des additions, des soustractions, des négations, les opérations logiques
AND, OR et NOT, et des comparaisons.L'addition
 Commencez par créer un circuit nommé 
ALU
. Ce circuit doit prendre en entrée :

 Un entier 

 encodé sur 8 bits,

 Un entier 

 encodé sur 8 bits,

 Un entier 

func
 encodé sur 16 bits qui indiquera l'opération à effectuer avec 

 et 

En sortie, 
ALU
 doit renvoyer uniquement le résultat de l'opération, c'est-à-dire un entier 
out
 encodé sur 8 bits.
Une ALU complète renverrai aussi un drapeau indiquant comment s'est déroulée
l'opération, par exemple, est-ce qu'il y a eu un dépassement de capacité ou une
division par zéro.
 L'entrée 
func
 doit être analysée de la façon suivante :

 Bits 0 à 5 (32 possibilités) : opération de base à effectuer. Dans notre cas, il


s'agit des opérations d'addition (0), de ET binaire (1) ou de comparaison (2).
On prévoit plus de bits que nécessaire pour pouvoir étendre notre 

ALU

 dans le futur.

 Bits 6, 7 et 8 : indique si il faut inverser les bits de 

 et 

out

 Bit 9 : indique si il faut injecter artificiellement une retenue pour l'addition,
 Bits 10 et 11 : indique l'opération de comparaison de base à effectuer (égal
pour le bit 10, ou inférieur strictement pour le bit 11).
 Bit 12 : indique si il faut inverser le résultat de l'opération de comparaison
effectuée.
 Bits 13 à 15 : réservés pour un usage futur.
De façon à décomposer 
func
, ajoutez un 
Splitter
 16 vers 3 3 suivi de dix 1. Les 6 premiers bits vont être utilisés pour sélectionner
l'opération effectuée en se servant de circuits 
8x8-mux
. Il faut donc décomposer ces 6 bits en 2 fois 3 bits puisque le sélecteur d'un 
8x8-mux
 est encodé sur 3 bits. Comme nous n'avons que 3 opérations, nous nous servirons
des 3 premiers bits, mais nous allons ignorer les 3 bits suivants réservés pour de
futurs usages. Les 10 bits qui suivent (6 à 16) vont permettre de gérer les autres
comportements du circuit 
ALU
.
 Ajoutez un 
8-add
 et connectez y directement les entrées 
a
 et 
b
 et la sortie 
out
. Ajoutez des 
HexDisplay
 avec des 
Splitter
 8 vers 4 4 pour afficher les valeurs de 
a

b
 et 
out
. Connectez aussi le bit 9 de 
func
 au 
cin
 de 
8-add
. Vérifiez que, quand ce bit 9 est à 0, votre circuit effectue bien une addition.La suite
de l'exercice qui permet de compléter le circuit 
ALU
 est optionnel et prend environ 90mn (en plus des 2/3h de travail personnel annoncé
en début d'exercice). Appuyez sur détails si vous voulez voir les questions.

Le registre (∼ 60mn)

Maintenant que nous sommes capables d'effectuer des calculs, il faut pouvoir
stocker dans le processeur des variables internes. Pour cela, nous construisons des
registres. Un registre est un circuit possédant principalement une entrée nommée 
in
 et une sortie nommée 
out
. Selon son état, le registre est capable de copier l'entrée 
in
 sur la sortie 
out
 ou de mémoriser la sortie 
out
 indépendamment de l'état de l'entrée. Ce phénomène de mémorisation permet au
registre de conserver un état dans le temps, et donc d'agir comme une mémoire.Le
verrou SR NAND
 Dans un nouveau circuit nommé 
SR-nand
, reproduisez le circuit constitué de deux portes NAND présenté en figure . Ce circuit
s'appelle un verrou SR (pour Set et Reset) NAND (SR NAND latch en anglais).

Le verrou SR NAND.
 Dans quels états sont Q et Q' quand S vaut 0 et R vaut 1 ? Et quand S vaut 1 et R
vaut 0 ?
 On suppose que, initialement, S = 0 et R = 1. Dans quel état est le circuit si S passe
à 1 ? Donnez exhaustivement l'ensemble des valeurs Q et Q' possibles en étudiant
les différentes propagations possibles de l'électricité. Pour cela, étudiez le cas où la
porte 
NAND
 du haut est la première à s'activer et le cas où la porte 
NAND
 du bas est la première à s'activer.
 On suppose que, initialement, S = 1 et R = 0. Dans quel état est le circuit si R passe
à 1 ? De la même façon étudiez les différentes possibilités de propagation de
l'électricité.Le bascule JK
 Comme vous avez du le constater, quand S = 1 et R = 1, on peut avoir Q = 1 et Q' =
0 si on vient de l'état S = 0 et R = 1, ou Q = 0 et Q' = 1 si on vient de l'état S = 1 et R
= 0. Cette propriété est très forte car elle signifie qu'on peut "bloquer" la sortie du
circuit à 1/0 ou 0/1 quand on passe S et R à 1. Pour exploiter cette propriété, on va
un petit peu améliorer notre circuit. Dans un nouveau circuit nommé 
JK-flip-flop
, reproduisez le circuit présenté en figure . Ce circuit s'appelle une bascule JK (JK
flip-flop en anglais). Le nom JK semble venir du nom du concepteur de ce circuit
(Jack Kirby). La bascule JK (Circuit
JK-flip-flop)
 Mettez l'entrée E (Enable) à 1. Dans quels états sont Q et Q' si J = 1 et K = 0 ? Dans
quels états sont Q et Q' si J = 0 et K = 1 ?
 Mettez E = 1, J = 0 et K = 1. Passez E à 0. Que se passe-t-il si vous changez les
valeurs de 
J
 ou de 
K
 ? Expliquez. De la même façon mettez E = 1, J = 1, K = 0. Passez E à 0. Que se
passe-t-il si vous changez les valeurs de 
J
 ou de 
K
 ? Expliquez.Le registre 1 bit
 Comme vous l'avez sûrement déjà compris, si K est l'opposé de J, alors, quand E
vaut 1, l'entrée J est recopiée dans Q. E permet aussi de bloquer le circuit : quand E
passe à 0, l'état de Q ne change plus, même quand J ou K change. En d'autres
termes, l'état du 
JK-flip-flop
 "mémorise" l'ancienne valeur de J. On va exploiter cette propriété pour construire
une première version du registre 1 bit. Créez un nouveau circuit que vous
nommerez 
1-reg
. Ajoutez deux entrées à ce circuit :
 in

 (1 bit) est l'entrée du registre,

 clock

 (1 bit) permet de bloquer le circuit (l'explication du nom 

clock

 viendra plus tard).

Ajoutez aussi une sortie un bit nommée 


out
. Enfin, créez un circuit qui (i) copie 
in
 dans 
out
 quand 
clock
 vaut 1 et (ii) laisse 
out
 inchangé quand 
clock
 vaut 0, même si 
in
 change.
 Ajoutez une entrée 1 bit nommée 
reinit
 à votre registre. Lorsque 
reinit
 vaut 0, le circuit doit fonctionner normalement (c'est-à-dire qu'il copie 
in
 dans 
out
 quand 
clock
 vaut 1 et conserve sa valeur quand 
clock
 vaut 0). En revanche, votre circuit doit propager la valeur de 
in
 vers 
out
 quand 
reinit
 vaut 1, et ceux, indépendamment de la valeur de 
clock
.
En construisant le circuit, vous devriez vous rendre compte que 
reinit
 et 
clock
 ont des rôles symétriques, c'est-à-dire qu'on pourrait inverser les rôles de 
reinit
 et 
clock
 sans changer le comportement du circuit. C'est normal. Même si les deux entrées
ont des rôles symétriques, elles ne serviront pas à la même chose dans la suite du
TP : 
reinit
 va servir à réinitialiser le registre à son état initial alors que 
clock
 va servir à propager la valeur de 
in
 dans 
out
.Le registre 8 bits
 Sur le modèle de 
8-add
, construisez un circuit nommé 
8-reg
 permettant de mémoriser une valeur sur 8 bits. Ce circuit doit avoir les entrées
suivantes :
 in

 sur 8 bits est l'état à l'entrée du registre,

 reinit

 sur 1 bit permet de réinitialiser le registre,

 clock

 sur 1 bit permet de propager la valeur de 

in

 vers 

out

 ou de mémoriser l'état courant.

Votre circuit doit avoir une unique sortie, 


out
, sur 8 bits et donnant l'état actuel du registre.
 Ajoutez à votre circuit une entrée 
v
 sur 8 bits. Cette entrée donne la valeur initiale du registre. Lorsque 
reinit
 vaut 1, cette valeur doit être directement propagée dans la registre et sur 
out
. Lorsque 
reinit
 vaut 0, l'entrée 
in
 doit être propagée dans le registre et sur 
out
 uniquement quand 
clock
 vaut 1.
 Ajoutez à votre circuit une entrée 
WE
 (Write Enable) qui, lorsqu'elle vaut faux, recopie 
out
 dans le registre quand 
clock
 passe à 1 au lieu de recopier 
in
. Pour cela, ajoutez un 
2x8-mux
 permettant de sélectionner soit 
in
, soit 
out
, et connectez la sortie de ce 
2x8-mux
 dans le 
x0
 de 
2x8-mux
 qui permet actuellement de sélectionner 
in
 ou 
v
.
 Avant d'aller plus loin, vérifiez que le comportement de votre registre est correct.
Pour cela, mettez la valeur 10001000 dans 
v
 et la valeur 
00000011
 dans 
in
, puis effectuez les tests suivants :

 Mettez 

clock

 à 0 et 

WE

 à 1,

 Passez 

reinit

 à 1 et vérifiez que 

out

 vaut 

 Passez 
reinit

 à 0 et vérifiez que 

out

 vaut toujours 

 Passez 

clock

 à 1 et vérifiez que 

out

 prend la valeur de 

in

 Passez 

WE

 à 0, puis modifiez la valeur de 

in

. Vérifiez que 

out

 conserve sa valeur, même quand vous activez/désactivez 

clock

Félicitations, même si nous serons amenés à modifier légèrement le registre 1 bit par
la suite, vous avez construit un superbe registre 8 bits ! Vous avez maintenant la
possibilité d'ajouter des variables internes à votre processeur !

Premier calculateur et horloge (∼ 40mn)


Nous avons maintenant tous les circuits permettant de construire un calculateur
simple. Dans cet exercice, nous construisons un compteur qui est incrémenté
régulièrement, c'est-à-dire une sorte de montre ou de réveil. Le circuit que vous
construisez à cette question pourra être détruit à la fin, mais il va vous permettre de
comprendre comment utiliser correctement les circuits que nous avons déjà créés.
Ce circuit va aussi nous permettre d'identifier un défaut dans notre conception du
registre 1 bit, ce qui va nous permettre de le corriger.
 Dans un nouveau circuit que vous appellerez 
test
, ajoutez un registre 8 bits ayant une valeur d'initialisation égale à 0. Pour créer des
entrées constantes, il faut aller dans l'onglet 
Input
 et sélectionner un 
ConstantVal
 (5ième icone). Par défaut, la constante est sur 1 bit. Pour lui affecter une valeur 0 sur 8
bits, double-cliquez dessus et saisissez 00000000.
 Modifiez votre circuit de façon à ce que 
WE
 soit toujours à vrai, et à ce que vous puissiez modifier les valeurs 
reinit
 et 
clock
 avec des entrées.
 À la sortie de votre registre, ajoutez un circuit permettant d'ajouter la constante 1 à
la valeur du registre et utilisez un 
HexDisplay
 pour afficher la valeur. Pensez aussi à connecter une valeur constante 0 au 
cin
 de l'additionneur. Vérifiez que quand vous passez 
reinit
 de 0 à 1 puis de 1 à 0, la sortie de l'additionneur est bien égal à 1.
 Nous allons enfin installer une première horloge dans notre processeur. Supprimez
l'entrée qui permet de modifier 
clock
 manuellement. À la place, allez dans l'onglet 
Sequential Elements
 et ajoutez une horloge (
Clock
, 8ième élément) que vous connecterez à l'entrée 
clock
 du registre. Cette modification ne devrait pas changer l'affichage de votre circuit qui
devrait rester à 1 (puisque votre circuit calcul toujours la somme entre la valeur du
registre, qui vaut 0, et la constante 1).
Une horloge est un circuit spécial qui passe régulièrement de 0 à 1 et de 1 à 0.
Quand on connecte une horloge à notre registre, la valeur de 
in
 du registre est propagée lorsque 
clock
 vaut 1 alors que la valeur précédente est conservée lorsque 
clock
 vaut 0. Il faut savoir que la fréquence à laquelle 
clock
 passe de 0 à 1 et revient de 1 à 0 s'appelle la fréquence du processeur. Aujourd'hui
cette fréquence se situe entre 2GHz et 3GHz (2 à 3 milliards de périodes par
seconde). C'est à cette cadence que le processeur propage les calculs dans les
registres, et c'est donc une métrique relativement pertinente pour estimer la vitesse
de calcul d'un processeur.Notre simulateur simule un processeur à 1Hz (demi-
période d'horloge de 500ms). Vous pouvez modifier cette fréquence dans les
propriétés du circuit (
Clock Time
).
 On souhaite maintenant incrémenter notre registre à chaque période. Commencez
par passer 
reinit
 à 1, ce qui va charger la valeur 0 dans le registre. Ensuite, connectez la sortie de
l'additionneur à l'entrée du registre (
in
). Pour le moment, votre additionneur devrait toujours vous donner la valeur 1
puisque, à cause de 
reinit
, le registre reste figé avec la valeur 0.
Maintenant, passez 
reinit
 à 0, ce qui devrait avoir comme effet de mettre à jour la valeur du registre à chaque
période d'horloge. Pour quelle raison le simulateur génère-t-il un message d'erreur (
Stack Overflow
) et pour quelle raison la valeur du registre semble sauter instantanément à une
valeur aléatoire ?Après avoir observé le phénomène, repassez 
reinit
 à 1 pour bloquer la valeur du registre à 0.
 Vous êtes confrontez à un problème classique : votre circuit modifie l'entrée du
calcul 8-add pendant le calcul lui-même. Techniquement, quand 
clock
 vaut 1, les informations se propagent dans votre circuit de la sortie du 8-add au
registre, puis de nouveau à l'entrée du 8-add, ce qui modifie le calcul pendant qu'il
est effectué. Comme vous n'avez aucune garantie que les résultats ont été propagés
à la même vitesse par tous les chemins (rappelez vous qu'un signal électrique se
propage à une vitesse donnée, à savoir une vitesse proche de la vitesse de la
lumière), vous pouvez facilement obtenir des valeurs aléatoires : certains bits sont
activés sur certains circuits et sont re-propagés avant d'autres. De façon à stabiliser
le circuit, il faut donc empêcher les entrées de changer pendant le calcul.
Comme solution, nous vous proposons de modifier le registre 
1-reg
. Avant toute chose, il faut savoir qu'il ne faut pas que vous détruisiez les entrées ou
les sorties de votre circuit actuel, sinon, vous serez obligé de refaire les connexions
dans le circuit 
8-reg
. Donc, sans détruire les entrées et les sorties que vous avez actuellement, nous
vous proposons d'ajouter une seconde bascule JK en contre-phase de la première.
Techniquement, on vous demande de mettre en œuvre le circuit présenté en figure .
Un registre 1-bit maître-escalve.
Dans ce circuit, la bascule JK de gauche, appelée bascule maître, est en phase
avec 
clock
, alors que celle de droite, appelée bascule esclave, est en contre-phase. En effet, si
on suppose que 
reinit
 vaut 0, l'entrée 
E
 du maître vaut 1 quand 
clock
 1, alors que l'entrée 
E
 de l'esclave vaut 1 quand 
clock
 vaut 0.
Avec cette modification, les valeurs sont propagées de 
in
 vers 
out
 en deux étapes. Quand 
clock
 est égal à 0, on est en phase de calcul : le processeur effectue des calculs avec les
valeurs 
out
 des registres, mais les résultats de ces calculs ne sont pas propagés dans les
registres puisque les bascules maîtres empêchent la propagation des valeurs (le 
E
 du maître vaut 0 puisqu'il est connecté directement à 
clock
).
Ensuite, lorsque 
clock
 passe à 1, on est en phase de propagation : on suppose que tous les calculs ont
atteint les entrées 
in
 des registres à cet instant. Les maîtres mémorisent alors les valeurs précédemment
calculées pour s'en servir comme d'entrées pour la prochaine phase de calcul.
Pendant cette phase de propagation, le circuit est dans un état stable : les esclaves
empêchent les valeurs de passer ce qui évite de modifier le résultat des calculs
pendant que le maître enregistre les résultats.
Enfin, lorsque 
clock
 repasse à 0, l'esclave enregistre la valeur du calcul précédent en propageant la
valeur déjà stockée par le maître. Cette propagation a pour effet de modifier la valeur
du registre et sa sortie, ce qui amorce donc une nouvelle phase de calcul avec de
nouvelles valeurs (notez que, comme précédemment expliqué, le maître devient
bloquant, ce qui garantie que, pendant ce nouveau calcul, les registres ne sont plus
modifiés).Dans le circuit que nous vous proposons, 
reinit
 réinitialise instantanément la valeur des registres puisque 
reinit
 est connecté en phase à la fois avec les maîtres et avec les esclaves. Ce
comportement est souhaité puisque lors d'une réinitialisation, on s'attend à ce que
tous les registres reprennent instantanément leurs valeurs initiales, et ceux sans
attendre une phase.Notez que la fréquence maximale d'un processeur est limitée par
la distance que le signal électrique doit parcourir en suivant le plus long chemin de
calcul entre les sorties et les entrées des registres. En effet, si la fréquence devient
trop grande par rapport à cette distance, il devient alors possible que certains
résultats atteignent les entrées mais pas d'autres, ce qui mettrait le processeur dans
un état non prédictible.
Vu cette limite, on voit donc qu'à chaque fois qu'on arrive à multiplier par deux le
nombre de transistors par cm2, on diminue par deux la distance à parcourir pour
effectuer un calcul, ce qui permet d'augmenter la fréquence du processeur d'autant.
Cette augmentation automatique de la fréquence avec la densité en terme de
transistors est restée vraie jusque vers 2005 : tous les 18 mois, on divisait grosso-
modo par deux la taille des transistors, ce qui permettait de multiplier la densité et
donc la fréquence des processeurs par deux (loi de Moore).
Aujourd'hui, la loi de Moore reste correcte car on arrive toujours à diminuer la taille
des transistors régulièrement. Toutefois, cette diminution de la taille de gravure ne se
traduit plus par une augmentation automatique de la fréquence pour une toute autre
raison : l'énergie consommée par un circuit augmente avec une loi en carré de la
fréquence. Au delà de 3GHz à 4GHz, l'énergie consommée par un ordinateur
deviendrait colossale (sans parler des problèmes de dissipation de la chaleur par
effet Joule).
 Dans le circuit 
test
 passez 
reinit
 à 0, ce qui devrait démarrer le calcul. Vérifiez que votre registre est bien incrémenté
à chaque période d'horloge (soit chaque seconde si vous avez gardé la demi-période
par défaut).Féclitations ! Vous savez réaliser de petits calculateurs !

Fonctionnement du processeur (∼ 30mn)

Nous avons maintenant créé pratiquement tous les circuits de base permettant de
construire un processeur complet. Avant de se lancer dans cette mise en œuvre
complète, il faut comprendre comment notre processeur va fonctionner.
Pour commencer, notre processeur a une mémoire de 16 cellules numérotées de 0 à
15. Une cellule mémoire contient un octet (une valeur sur 8 bits), et nous
réutiliserons les registres 8 bits pour mettre en œuvre les cellules.
Ensuite, notre processeur possède 8 registres internes numérotés de 0 à 7. Chaque
registre fait 8 bits, notre processeur est donc un processeur 8 bits. Pour bien
comprendre la différence entre les registres internes et la mémoire, il faut savoir que
la mémoire est un composant séparé du processeur et que notre processeur ne peut
que :

 effectuer des calculs sur les registres internes,


 lire une cellule mémoire et stocker sa valeur dans un registre interne
(instruction 

LD

),

 écrire la valeur d'un registre interne dans une cellule mémoire (instruction 

ST

).

Trois des registres du processeur ont un rôle particulier qui sera expliqué dans la
suite. Les cinq autres sont libres et permettent d'effectuer des calculs.
Le premier registre particulier est le 7ième registre qui se nomme 
pc
 (Program Counter). Ce registre indique le numéro de cellule mémoire qui contient la
prochaine instruction à exécuter. Notre processeur offre deux types d'instructions.

 Forme triplet : op out, i0, i1


 Forme accumulateur : op out, val

Où :
 op

 est un numéro d'instruction, par exemple, la valeur 2 correspond à l'addition,

 out

 est le numéro de registre modifié par l'instruction (appelé registre de sortie de


l'instruction),

 i0

 est le premier numéro de registre d'entrée de l'instruction,

 i1

 est le second numéro de registre d'entrée de l'instruction,


 val

 est une valeur directe sur 8 bits.

Quand l'instruction est sous la forme triplet, elle effectue un calcul avec les registres 
i0
 et 
i1
, puis stocke le résultat dans le registre 
out
. Par exemple, l'instruction 
2 3 1 2
 sous la forme triplet est une addition (puisque 
op
 vaut 2) qui ajoute la valeur du registre numéro 1 avec la valeur du registre numéro 2
puis stocke le résultat dans le registre numéro 3.
Quand l'instruction est sous forme accumulateur, elle effectue un calcul avec un
registre accumulateur (le registre 
out
) et la valeur directe encodée sur 8 bits. Par exemple, l'instruction 
2 3 1
 sous la forme accumulateur est une addition (puisque 
op
 vaut 2) qui ajoute 1 au registre numéro 3.
Pour encoder une instruction, nous avons besoin de 16 bits :

 Premier octet 
o op

 est encodé sur 4 bits (bits 0 à 3), ce qui permet de définir jusqu'à 32
instructions. Dans notre processeur, nous ne définirons que 3
instructions de base, n'hésitez pas à en ajouter en fonction de vos
envies après avoir terminé l'exercice,

o Le bit 4, appelé 

accu?

 indique si l'instruction est sous la forme accumulateur ou triplet,

o Les bits 5 à 7 donnent le numéro du registre de sortie (avec 3 bits, on


peut encoder 8 valeurs),
 Deuxième octet :
o Dans le cas d'une instruction sous la forme accumulateur, cet octet
contient 

val

,
o Sinon, cet octet se décompose de la façon suivante :
 Les bits 0 à 2 donnent 

i0

 Les bits 3 à 5 donnent 

i1

 Les bits 6 à 7 ne sont pas utilisés.

En sachant que l'instruction 0 est un 


LD
, 1 est un 
ST
 et 2 est un 
ADD
, voici quelques exemples d'instructions :
Octet 1 : 000 1 0010 => op = ADD, accu? = 1, out = 0
Octet 2 : 1111 1111 => val = 15
=> ajoute 15 au registre numéro 0 (out)

Octet 1 : 010 0 0010 => op = ADD, accu? = 0, out = 2


Octet 2 : 00 001 100 => i0 = 4, i1 = 1
=> ajoute la valeur du registre numéro 4 (i0) à celle du
registre numéro 1 (i1),
puis stocke le résultat dans le registre numéro 2 (out)

Octet 1 : 100 0 0000 => op = LD, accu? = 0, out = 4


Octet 2 : 00 000 010 => i0 = 2, i1 = 0
=> charge la cellule mémoire dont le numéro est stocké dans
le registre numéro 2 (i0),
puis stocke cette valeur dans le registre numéro 4 (out)
(LD n'utilise pas i1)

Octet 1 : 100 0 0001 => op = ST, accu? = 0, out = 4


Octet 2 : 00 000 010 => i0 = 2, i1 = 0
=> écrit le contenu du registre numéro 0 (i1) dans la
cellule mémoire
ayant pour numéro la valeur du registre numéro 0 (i0)
(ST n'utilise pas out puisque cette instruction ne
modifie aucun
registre du processeur, uniquement des cellules mémoires)
De façon à stocker les deux octets d'une instruction dans le processeur, nous
utilisons le 5ième et le 6ième registre que nous nommons 
insnl
 (insn low) et 
insnh
 (insn high).
Finalement la boucle de notre processeur devient :

 charge la cellule mémoire indiquée dans 

pc

 dans le registre 

insnl

 ajoute 1 à 

pc

 charge la cellule mémoire indiquée dans 

pc

 dans le registre 

insnh

 ajoute 1 à 

pc

 exécute l'instruction définie par les 8+8 bits de 

insnl

 et 

insnh

.
Il n'y a pas de question dans cet exercice.

Registres et mémoire (∼ 90mn)

Dans cet exercice, nous concevons les circuits qui permettent de gérer les registres
et la mémoire de notre processeur. D'un point de vu technique, nous mettons en
œuvre les registres du processeur et la mémoire avec des registres. Bien que le
circuit de base utilisé pour construire les registres et la mémoire soit le même, il
existe trois différences fondamentales entre ces objets :

 La mémoire se trouve en dehors du processeur, ce qui signifie que la


fréquence de la mémoire n'est pas forcément la même que celle du
processeur. Dans notre exercice, de façon à simplifier, nous utiliserons la
même horloge dans les deux cas. Mais dans un processeur réel, il faut mettre
en place des mécanismes de synchronisation lors des accès à la mémoire, ce
qui ralentit l'accès à la mémoire par rapport à un accès registre.
 Alors qu'à chaque cycle processeur, il est possible de lire et écrire
simultanément plusieurs registre, le circuit d'une mémoire est prévu pour
permettre au plus une unique lecture et une unique écriture pendant un cycle
horloge (horloge de la mémoire).
 À cause de problèmes d'encombrement, il n'est pas possible de mettre une
grande quantité de registres dans un processeur (au mieux quelques dizaines
ou centaines de kilo-octets stockés sous la forme de registres dans un cœur
d'un processeur). En revanche, une mémoire ne contient que peu de circuits
pour effectuer des calculs et il est possible de mettre plusieurs barrettes
mémoires sur une carte mère. Il est donc beaucoup plus facile de construire
de grandes quantités de mémoire (jusqu'à plusieurs tera-octets aujourd'hui
dans une seule machine).

Comme les registres du processeurs et la mémoire sont construits au départ avec


des registres, nous allons commencer par réaliser un assemblage de 8 registres,
nommé 
8x8-regs
 (8 registres de 8 bits) qui nous servira de base pour construire les registres du
processeur et la mémoire dans la suite de l'exercice.
Le 
8x8-regs
 doit pouvoir être utilisé pour construire les registres du processeur. Quand on
analyse notre jeu d'instruction, on voit que :

 Une instruction peut lire jusqu'à 2 registres pendant un cycle. C'est le cas des
instructions sous la forme triplet,
 Une instruction peut écrire au plus un registre pendant un cycle. C'est le cas
d'une instruction de calcul qui utilise l'ALU, comme l'instruction 

ADD

 (qu'elle soit sous la forme triplet ou non), mais aussi de l'instruction 

LD
 qui permet de lire une cellule mémoire et de stocker sa valeur dans un
registre.

 Une instruction peut ne pas écrire dans un registre, comme l'instruction 

ST

 qui ne fait que écrire la valeur d'un registre dans une cellule mémoire. Il faut
donc être capable de désactiver les écritures sur le 

8x8-regs

 au besoin.

Le démultiplexeur

 Pour sélectionner jusqu'à deux registres d'entrées d'une instruction dans le 
8x8-regs
, nous disposons déjà du circuit 
8x8-mux
. En revanche, pour sélectionner le registre écrit par une instruction, il nous manque
un circuit. En effet, il faut être capable d'activer l'entrée WE de l'un des huit registres
du 
8x8-regs
 en fonction d'un numéro de registre encodé sur 3 bits (3 bits suffisent pour énumérer
8 = 23 valeurs).
À cette question, on vous demande donc de mettre en place un démultiplexeur. Un
démultpléxeur possède une entrée nommée 
sel
 sur N bits et 2N sorties numérotés de 
x0
 à 
xn-1
. Si l'entrée vaut K, alors le démultiplexeur passe la sortie 
xK
 à vrai et laisse les autres à faux. Un démultiplexeur a aussi une seconde entrée
nommée 
enable
 qui, lorsqu'elle vaut 0, désactive le multiplexeur. Dans ce cas, le démultiplexeur
n'active aucune sortie.
Mettez en œuvre le circuit 
8-demux
 dans lequel 
N=3
 (c'est-à-dire 2N = 8). 

Un banc de 8 registres

 Nous pouvons maintenant construire le 


8x8-regs
 qui assemble 8 registres. On souhaite avoir les entrées suivantes :
 v0

 à 

v7

 sont les valeurs initiales des registres (8 bits),

 si0

 et 

si1

 sont les numéros des deux registre lus par l'instruction courante (3 bits),

 sout

 est le numéro de registre écrit par l'instruction (3 bits), 

vout

 la valeur qu'on souhaite écrire (8 bits) et 

WE

 indique si les écritures sont activées (1 bit),

 reinit

 permet de réinitialiser les huit registres aux valeurs initiales (

v0

 à 

v7

) (1 bit),

 clock

 est l'horloge (1 bit).

En sortie, le circuit doit renvoyer :


 r0

 à 
r7

 sont les valeurs courantes des 8 registres,

 vi0

 et 

vi1

 sont les valeurs des registres 

si0

 et 

si1

Les registres du processeur

 On construit maintenant le circuit 


REGS
 qui contient les registres du processeur. Construire ce circuit à partir de 
8x8-regs
 est très simple : il suffit d'initialiser chacun des registres à la valeur 0 puis de
renommer 
r5
 en 
insnl

r6
 en 
insnh
 et 
r7
 en 
pc
. Le autres entrées ou sorties doivent conserver le même nom.Pendant que vous
mettez en œuvre 
REGS
, profitez-en pour vérifier que votre mise en œuvre de 
8x8-regs
 est correcte. En effet, identifier un éventuel bug dans 
8x8-regs
 plus tard sera beaucoup plus complexe.
 Nous pouvons maintenant mettre en œuvre le circuit 
MEM
. Ce circuit assemble 2 circuits 
8x8-regs
 pour simuler une mémoire de 16 octets, ce qui sera largement suffisant pour faire
des tests. 
MEM
 prend en entrée :
 address

 sur 8 bits : le numéro de cellule mémoire (registre) accédé. Même si on n'a


que 16 octets de mémoire, on prévoit 

 bits pour 

address

 puisque notre processeur est un 8 bits,

 data

 sur 8 bits : la valeur écrite dans l'adresse si l'instruction en cours est une
écriture mémoire (instruction 

ST

),

 WE

 sur 1 bit : indique si l'instruction en cours est une écriture,

 reinit

 sur 1 bit : permet de réinitialiser la mémoire à sa valeur initiale,

 clock

 sur 1 bit : l'horloge.

En sortie, 
MEM
 renvoie la valeur se trouvant à l'adresse 
address
.
Initialement, nous vous proposons de placer un programme directement dans la
mémoire. Ce programme assez simple incrémente la 15ième cellule mémoire. Si on
suppose que la cellule 15 contient la valeur d'une variable 
x
 initialisée à 128 (0x80 en hexadécimal), le code exécuté par notre programme serait
à peu près celui là en Java :
do {
x = x + 1;
} while(true);
À bas niveau, voici les valeurs initiales que doit contenir la mémoire pour exécuter ce
programme :
Octet 0 : 000 1 0010
Octet 1 : 00001111 => ajoute 15 à r0 (r0 valant initialement
0, r0 vaut 15 en sortie, c'est-à-dire
le numéro de la cellule contenant la
variable
x
)
Octet 2 : 001 0 0000
Octet 3 : 00 000 000 => charge la cellule se trouvant en r0
dans r1
(charge la 15ième cellule car r0 = 15)
Octet 4 : 001 1 0010
Octet 5 : 00000001 => ajoute 1 à r1
Octet 6 : 000 0 0001
Octet 7 : 00 001 000 => écrit r1 dans la cellule r0 (adresse
de
x
)
Octet 8 : 111 1 0010
Octet 9 : 11111000 => ajoute 248 à pc
(comme pc = 10 avant d'exécuter
l'instruction, pc + 248 = 258 modulo 256 = 2
=> saute à l'octet 2, ce qui permet de
boucler)
Octets 10 à 14 : 00000000
Octet 15 : valeur initiale de x, mettez 01110000 (0x80 en
hexadécimal)

Le processeur (∼ 120mn)

Nous pouvons enfin attaquer la phase finale de conception du processeur.

Le microcode

 À cette question, nous créons le circuit nommé 


MICROCODE
 qui génère les instructions à exécuter. Comme précédemment expliqué, la boucle
principale de notre processeur consiste en lire la cellule se trouvant en 
pc
 et la stocker dans le registre 
insnl
, ajouter 1 à 
pc
, lire la cellule se trouvant en 
pc
 et la stocker dans le registre 
insnh
, ajouter 1 à 
pc
, exécuter 
insnl
/
insnh
 et recommencer.
Plutôt que de créer des cas spéciaux d'additions ou de lecture pour effectuer ces
opérations, nous allons générer manuellement les 4 instructions qui précédent
l'exécution de 
insnl
/
insnh
 avant de générer l'instruction encodée dans 
insnl
/
insnh
. En interne, de façon à simuler ces instructions, nous allons utiliser un registre 8 bits
nommé 
mini-pc
. La valeur de ce registre indique quelle instruction il faut générer : l'une des quatre
instructions qui permet de lire 
insnl
/
insnh
 ou l'instruction 
insnl
/
insnh
.
Techniquement, ce circuit prend en entrée :
 insnl

 et 

insnh

 sont les valeurs se trouvant dans les registres 

insnl

 et 

insnh
. Après avoir exécuté le chargement de ces registres, le circuit va simplement
renvoyer ces valeurs,

 reinit

 et 

clock

 pour piloter le registre 

mini-pc

De façon à sélectionner l'instruction à exécuter en fonction de 


mini-pc
, il faut utiliser deux 
8x8-mux
 (l'un pour la partie 8 premiers bits de l'instruction et l'autre pour la 8 bits suivants)
pilotés par les 3 bits de poids faible de 
mini-pc
. Ces 
8x8-mux
 doivent générer les sorties suivantes pour le circuit :
mini-pc = 0 : insnl = 101 0 0000
insnh = 00 000 111 => ld insnl, pc
mini-pc = 1 : insnl = 111 1 0010
insnh = 00000001 => add pc, 1
mini-pc = 2 : insnl = 110 0 0000
insnh = 00 000 111 => ld insnh, pc
mini-pc = 3 : insnl = 111 1 0010
insnh = 00000001 => add pc, 1
mini-pc = 4 : insnl = insnl donné en entrée du circuit
insnh = insnh donné en entrée du circuit
Enfin, à chaque cycle, votre circuit doit incrémenter 
mini-pc
, et lorsqu'il vaut 5, le faire revenir à 0 (en utilisant 
reinit
 par exemple). Notez que si 
mini-pc
 vaut 5, c'est que les bits 0, 1 et 2 valent respectivement 1, 0, 1.

Le décodeur d'instruction

 Une fois que notre circuit 


MICROCODE
 a généré une instruction à exécuter, il faut la décoder, c'est-à-dire générer toutes les
informations qui vont permettre de piloter les autres circuits.
Créez un nouveau circuit nommé 
DECODER
 qui prend en entrée :
 insnl

 : la partie basse de l'instruction à analyser,

 insnh

 : la partie haute de l'instruction à analyser,

Ce circuit doit générer les sorties suivantes :


 func

 sur 16 bits. Il s'agit de la fonction que doit exécuter le circuit 

ALU

. Comme notre jeu d'instruction ne contient qu'une addition, il suffit de générer


la valeur 0 sur 16 bits à partir d'une constante.

 wm?

 est vrai quand l'instruction écrit en mémoire, c'est-à-dire quand l'instruction


exécutée est un 

ST

 (valeur 1). Pour identifier ce cas, il faut isoler les bits qui donnent l'instruction,
c'est-à-dire les 4 premiers de 

insnl

, puis utiliser un 

8-demux

 pour savoir si l'instruction est l'instruction 1 (

ST

). Un seul 

8-demux

 piloté par les 3 premiers bits du numéro d'instruction est nécessaire puisque
nous n'utilisons pas le 4ième bit du numéro d'instruction.

 wr?
 est vrai lorsque l'instruction écrit dans un registre, c'est-à-dire si l'instruction
n'est pas 

ST

 alu?

 indique quelle valeur doit être utilisée pour écrire dans un registre quand 

wr?

 est vrai : la valeur renvoyée par le circuit 

MEM

 quand 

alu?

 vaut 0 ou la valeur renvoyée par le circuit 

ALU

 quand 

alu?

 vaut 1. Techniquement, seule l'instruction 

LD

 utilise la sortie de 

MEM

 pour l'écrire dans un registre. La valeur 

alu?

 est donc égal à l'inverse de la sortie 

x0

 du 

8-demux

 que vous avez utilisé pour identifier l'instruction 


ST

 cst?

 indique si la seconde opérande d'entrée de l'instruction est une constante.


Cette valeur est donc vraie si et seulement si 

accu?

 (4ième bit de 

insnl

) est vrai. En effet, dans ce cas, l'instruction écrite sous la forme 

op rout, val

 effectue un calcul à partir de 

rout

 et de la constante val au lieu d'effectuer un calcul à partir de deux registres.

 sout

 donne le registre écrit par l'instruction (bit 5, 6 et 7 de 

insnl

).

 si0

 donne le premier registre d'entrée de l'instruction, c'est-à-dire l'adresse pour


les instructions 

LD

 et 

ST

, et la première opérande pour 

ALU

. Dans le cas d'une instruction au format triplet (c'est-à-dire quand le bit 4 de 
insnl

 vaut 0), il faut renvoyer les bits 0, 1 et 2 de 

insnh

. Dans le cas d'une instruction au format accumulateur, il faut renvoyer 

sout

 si1

 donne le second registre d'entrée de l'instruction, c'est-à-dire la donnée à


écrire pour 

ST

 ou la seconde opérande pour 

ADD

. Il faut donc renvoyer les bits 3, 4 et 5 de 

insnh

 vi1

 donne la valeur constante qui doit être utilisé en remplacement de 

si1

 dans le cas où 

cst?

 vaut 1. Il faut renvoyer 

insnh

Le processeur

 Vous pouvez enfin mettre en œuvre votre processeur. Vous avez besoin de deux
entrées : 
reinit
 et 
clock
. Tout le reste sera généré par les autres circuits. Vous avez besoin des circuits
suivants :

 Un circuit 

MICROCODE

 pour générer les instructions,

 Un circuit 

DECODER

 connecté à 

MICROCODE

 pour interpréter les instructions,

 Un circuit 

REGS

 qui contient les registres. Ce circuit prend en entrée les 

si0

si1

sout

 et 

wr?

 généré par 

DECODER

 (l'entrée 

vout

 de ce circuit sera donnée par un autre circuit).


 Un circuit 

2x8-mux

 permettant de sélectionner la deuxième opérande de l'instruction. Quand 

cst?

 vaut 0, cette opérande est donnée par le 

vi1

 de 

REGS

 (instruction au format triplet) et par le 

vi1

 de 

DECODER

 sinon (format accumulateur).

 Un circuit 

ALU

 qui utilise le 

vi0

 de 

REGS

 comme première valeur, la sortie du 

2x8-mux

 précédent comme seconde valeur, et qui exécute la fonction 

func

 générée par 

DECODER
.

 Un circuit 

MEM

 qui utilise le 

vi0

 comme adresse, la sortie du 

2x8-mux

 précédent comme 

data

 et le 

wm?

 de 

DECODER

 comme entrée 

WE

 Un circuit 

2x8-mux

 qui prend en entrée la sorties de 

MEM

 en 

x0

, la sortie de 

ALU

 pour 
x1

, et la sortie 

alu?

 de 

DECODER

 comme sélecteur. Pour terminer le processeur, il suffit de connecter la sortie


de ce circuit au 

vout

 de 

REGS

 puisque, si 

wr?

 est vrai, c'est la sortie de ce circuit qu'on veut enregistrer dans le registre de
sortie.

Ce circuit étant légèrement complexe, surtout parce que des bugs de mise en œuvre
provenant d'autres circuits vont apparaître, nous vous conseillons vivement d'utiliser
des 
HexDisplay
 pour afficher l'état des registres, la valeur 
v0/adresse
 utilisée en entrée de 
ALU
/
MEM
, la valeur 
v1/data
 utilisée en entrée de 
ALU
/
MEM
 et la valeur 
vout
 envoyée dans 
REGS
. Vous pouvez aussi afficher la case mémoire 15 pour voir l'état de cette case
mémoire évoluée au cours du temps (il faut alors ajouter une sortie à 
MEM
 pour pouvoir connaître cette valeur). Au démarrage, cette valeur devrait prendre la
valeur 0x80 et être incrémentée régulièrement pas le programme. N'hésitez pas
aussi à afficher l'état du 
minipc
 de 
MICROCODE
 en ajoutant une sortie à ce circuit. Bon courage !Si vous êtes arrivé au bout de cet
exercice, bravo ! Vous savez construire un processeur de bout en bout !Depuis la fin
des années 70, on n'écrit plus de processeur directement avec des portes logiques,
sauf pour comprendre sa mise en œuvre comme dans cet exercice. En effet, dans
notre exercice, nous avons fait l'hypothèse que 
MEM
 avait la même fréquence que le reste du processeur, et que 
ALU
 était capable d'effectuer un calcul en moins d'un demi-cycle. Dans des machines
réelles, ce n'est pas le cas. Il faut donc synchroniser des circuits qui effectuent des
opérations à différentes vitesses avec la même horloge, voir des circuits qui ont des
horloges différentes quand on communique entre la mémoire et le processeur (on
parle de circuit multi-clock domains). Comme gérer ces synchronisations à la main
devient rapidement très difficile, on utilise aujourd'hui des langages plus expressifs
comme VHDL ou Verilog.
 

Telecom SudParis
Institut Polytechnique de Paris
9, rue Charles Fourier
F-91011 Evry Cedex France

19, Place Marguerite Perey


F-91120 Palaiseau Cedex France

Ⓒ 2020 Télécom SudParis - All rights reserved

Vous aimerez peut-être aussi