5 Codage EN VHDL

publicité
UN MINI-PROCESSEUR
Projet de VHDL
Gregory Heinrich/Maël Le Berre
Historique du document
Version
Date
Modification
V0.01
26.10.2002
Document de référence initial
V0.02
29.10.2002
Addition d’informations sur l’architecture et définition des
entités
V0.03
10.11.2002
Addition des codes source et chronogrammes de simulation
V1.01
12.11.2002
Document rendu
Projet de VHDL – un mini-processeur
-1-
TABLE DES MATIERES
UN MINI-PROCESSEUR...................................................................................... 1
TABLE DES MATIERES ............................................................................................................... 2
CONTEXTE ..................................................................................................................................... 4
1
OBJECTIFS............................................................................................................................... 5
1.1
1.2
1.3
UN CODE REUTILISABLE ET COMMENTE ................................................................................. 5
UN CODE MODULAIRE POUR FACILITER LE TRAVAIL EN EQUIPE ............................................. 5
L’AVANTAGE DE L’OUTIL INFORMATIQUE ............................................................................. 5
2
CAHIER DES CHARGES ....................................................................................................... 6
3
METHODOLOGIE POUR LE CODAGE EN VHDL ....................................................... 10
3.1 NOMENCLATURE DES SYMBOLES ......................................................................................... 10
3.1.1 Noms de fichiers ........................................................................................................... 10
3.1.2 Noms de types et de signaux ........................................................................................ 10
3.2 TYPES DE DONNEES UTILISES ............................................................................................... 10
Fichier upp_util_types.vhdl.conf ............................................................................................... 11
3.3 PACKAGES ........................................................................................................................... 13
Fichier upp_util_tools.vhdl ....................................................................................................... 13
4
ARCHITECTURE DU COMPOSANT ................................................................................ 14
4.1
4.2
4.3
5
L’UNITE ARITHMETIQUE ET LOGIQUE (UPP_ALU) ................................................................. 14
LE DECODEUR D’INSTRUCTIONS (UPP_DECODER) ................................................................ 15
LE PILOTE DE REGISTRES (UPP_REGDRIVER) ........................................................................ 15
CODAGE EN VHDL .............................................................................................................. 17
Projet de VHDL – un mini-processeur
-2-
5.1 OUTILS POUR L’AIDE A LA COMPILATION ............................................................................. 17
5.1.1 Script pour le paramétrage des constantes du système ............................................... 17
5.1.2 Fichier Makefile ........................................................................................................... 19
5.2 ENTITES UTILISEES .............................................................................................................. 19
5.2.1 Upp............................................................................................................................... 19
5.2.2 Upp_alu........................................................................................................................ 19
5.2.3 Upp_decoder ................................................................................................................ 19
5.2.4 Upp_regdriver.............................................................................................................. 20
5.3 SOURCES VHDL DES COMPOSANTS MODELISES .................................................................. 20
5.3.1 Upp.vhdl ....................................................................................................................... 20
5.3.2 Upp_alu.vhdl ................................................................................................................ 22
5.3.3 Upp_decoder.vhdl ........................................................................................................ 23
5.3.4 Upp_regdriver.vhdl ...................................................................................................... 25
6
SIMULATIONS ...................................................................................................................... 29
6.1
6.2
6.3
6.4
7
SIMULATION DU CIRCUIT DE DECODAGE (UPP_DECODER) .................................................... 29
SIMULATION DU CIRCUIT D’OPERATIONS LOGIQUES ET ARITHMETIQUES (UPP_ALU)............ 29
SIMULATION DU PILOTE DE REGISTRES (UPP_REGDRIVER) ................................................... 30
SIMULATION DU COMPOSANT PRINCIPAL UPP....................................................................... 31
SYNTHESE ............................................................................................................................. 32
7.1 UPP_ALU ............................................................................................................................. 33
7.2 UPP_DECODER ..................................................................................................................... 34
7.3 UPP_REGDRIVER .................................................................................................................. 35
7.4 UPP ...................................................................................................................................... 36
7.4.1 Upp, avec sa hiérarchie de composants conservée ..................................................... 36
7.4.2 Upp, hiérarchie dissoute .............................................................................................. 36
8
POUR ALLER PLUS LOIN… .............................................................................................. 38
8.1
8.2
8.3
PROGRAM COUNTER ............................................................................................................ 38
MEMOIRE CACHE ................................................................................................................. 38
PIPELINE .............................................................................................................................. 38
Projet de VHDL – un mini-processeur
-3-
CONTEXTE
Le VHDL (Very High speed integrated circuits Description Language) est un langage de
description matériel permettant de décrire la structure d’un design électronique, en la décomposant
en sous-blocs et en spécifiant comment ces sous-blocs sont reliés. D’autre part, il permet de décrire
le corps d’un bloc par l’intermédiaire de fonctions usuelles utilisées dans des langages de
programmation informatique.
Décrire un composant en VHDL permet de réduire le cycle de conception, en offrant un design
facilement simulable et plus facile à valider. D’autre part, les composants créés sont facilement
réutilisables et modifiables.
Dans le cadre de l’unité VHDL de I4, les étudiants doivent réaliser un projet en VHDL. Ce rapport
décrit la façon dont le projet a été mené.
Projet de VHDL – un mini-processeur
-4-
1
OBJECTIFS
Lorsque l’on commence un projet, il faut avoir des objectifs clairs. Nous nous sommes fixés ici
l’objectif de montrer aussi clairement que possible comment le VHDL a changé la donne en
matière de conception électronique. En particulier, il nous est paru important d’insister sur les
points suivants :
1.1 Un code réutilisable et commenté
Le code VHDL que nous écrirons sera paramétrable autant que possible. Nous maintiendrons un
fichier de constantes permettant de changer toutes les constantes du composant : taille des
registres, nombre de registres, nombre d’instructions, etc.
D’autre part, le code sera lisible et commenté, de manière à en faciliter la compréhension pour une
personne étrangère au projet.
1.2 Un code modulaire pour faciliter le travail en équipe
En VHDL, il est très facile de décomposer un composant en sous-composants. Chacun de ces souscomposants devient un composant à part entière et doit répondre à un sous-cahier des charges. Si
chacun des sous-composants est fidèle à son sous-cahier des charges, alors les membres du groupe
de projet peuvent travailler indépendamment les uns des autres et rassembler leurs descriptions une
fois leur mission accomplie.
1.3 L’avantage de l’outil informatique
Les fichiers VHDL sont des fichiers texte à part entière. On peut donc profiter de l’avantage offert
par l’environnement de développement pour programmer des scripts informatiques qui traitent les
fichiers sources au préalable. Dans notre exemple, nous utiliserons par exemple un script Perl qui
permet de remplacer dans le code VHDL des constantes spécifiées dans un fichier principal de
constantes et de faire certains calculs comme des calculs de logarithmes (utilisés notamment pour
déterminer le nombre de fils nécessaires pour coder un nombre compris dans un intervalle de
valeurs). Nous aurons aussi un fichier makefile qui contiendra toutes les informations sur la
compilation du code et les dépendances entre fichiers.
Projet de VHDL – un mini-processeur
-5-
2
CAHIER DES CHARGES
Projet de VHDL – un mini-processeur
-6-
Projet de VHDL – un mini-processeur
-7-
Projet de VHDL – un mini-processeur
-8-
Projet de VHDL – un mini-processeur
-9-
3
METHODOLOGIE POUR LE CODAGE EN VHDL
3.1
Nomenclature des symboles
Afin de s’y retrouver dans un projet, il est souvent utile de se fixer des règles pour la nomenclature
des symboles utilisés. Dans la suite, on notera que « upp » est l’acronyme du sujet du projet : « un
petit processeur ».
3.1.1
Noms de fichiers
Les noms de fichiers VHDL utilisés sont de la forme :
- Upp.vhdl, pour le fichier contenant l’entité et l’architecture du composant principal,
- Upp_util_*.vhdl pour les fichiers contenant les packages utilisés,
- Upp_*.vhdl pour les fichiers contenant les sous-composants.
Enfin, nous utilisons un fichier générique de constantes nommé project.conf, et un fichier modèle
pour le remplacement automatique par script des constantes appelé upp_util_types.vhdl.conf. Ces
deux fichiers sont décrits dans la section suivante.
3.1.2
Noms de types et de signaux
Pour tous les symboles, le caractère « _ » permet de lier les mots composant le nom du symbole
lorsque c’est nécessaire.
Les signaux et les types sont écrits en minuscule. Les types définis dans le projet comportent tous
la mention « upp_ » au début.
Les constantes génériques du système sont notées en majuscules.
Le mot clé « accumulateur » désignera le registre utilisé pour stocker les résultats d’une opération
sur des registres opérandes souvent notés « operande1 » et « opérande2 ».
3.2
Types de données utilisés
Il est très important de se mettre d’accord sur les types de données utilisés car cela permet ensuite
d’échanger des informations homogènes. Nous avons souhaité dans ce projet emprunter une
approche proche de celle que l’on pourrait utiliser pour programmer dans un langage de
programmation. Par exemple, nous utilisons des structures de données (types record) pour
modéliser des instructions. Nous pensons que ceci permet d’abstraire beaucoup mieux le problème,
sans pour autant augmenter la complexité du circuit synthétisé. Les structures de données ne sont
Projet de VHDL – un mini-processeur
- 10 -
rien d’autre que des fils au niveau synthétisé, mais au niveau du source VHDL, elles nous ont
permis d’écrire un code plus concis et plus proche du modèle.
D’autre part, pour obtenir un circuit synthétisable, nous avons veillé à ce que tous les signaux
d’entrée/sortie du composant soient des std_logic, ou des std_logic_vector.
Quelques informations sur les principaux types utilisés :
- upp_reg représente un registre, dont la taille est paramétrée par REG_SIZE,
- upp_reg_array est un tableau de registres (le nombre de registres est paramétré par
REG_COUNT). C’est la banque de registres utilisés dans le composant upp_regdriver pour
stocker les données,
- upp_reg_addr_bus, un registre contenant suffisamment de bits pour permettre d’adresser tous
les registres du composant,
- upp_opcode, une énumération sur les stypes d’instructions supportés (ex : op_null, op_add,
…),
- upp_instruction est une structure de données contenant l’opcode de l’instruction (upp_opcode)
et les adresses des deux registres opérandes,
- upp_regdrivingmode, une structure de données utilisée dans upp_regdriver pour déterminer le
mode de transport des registres. Cette structure contient l’index du registre, ainsi qu’un flag
boolean permettant de déterminer si les bus data_in, data_out du composant principal sont
impliqués dans le transport,
- upp_overalldrivingmode, une structure contenant les informations sur le transport de chacun
des registres accumulateur, operande1 et opérande2 utilisés pour communiquer entre
upp_regdriver et upp_alu.
Fichier upp_util_types.vhdl.conf
-- ----------- ESIEE I4 --------------- EL411 : Projet de VHDL
-- Un Petit Processeur ( upp )
-- Gregory HEINRICH / Mael LE BERRE
--------------------------------------- upp_type :
-- contantes et types generaux utilises dans le processeur upp.
-- Ces types permettent de controler entierement la taille de ses attributs.
library ieee ;
use ieee.std_logic_1164.all, ieee.numeric_std.all ;
package upp_types is
------------ CONSTANTES :
-- Taille des registres en bit :
constant REG_SIZE : integer := 16 ;
-- Nombre de registres :
Projet de VHDL – un mini-processeur
- 11 -
constant REG_COUNT : integer := 8 ;
-- Largeur du bus d'adresse des registre :
-- ( typiquement : ADDR_BUS_SIZE = LOG2( REG_COUNT ) )
constant ADDR_BUS_SIZE : integer := 3 ;
-- Taille des codes operations :
-- ( typiquement : OPCODE_BIT_COUNT = LOG2( nombre de code operation )
)
constant OPCODE_BIT_COUNT : integer := 3 ;
-- Taille d'une instruction en bit :
constant INSTRUCTION_SIZE : integer := OPCODE_BIT_COUNT +
2*ADDR_BUS_SIZE ;
------------ TYPES :
-- Type d'un registre :
subtype upp_reg is signed( REG_SIZE-1 downto 0 ) ;
-- Type de la banque de registre :
type upp_reg_array is array( REG_COUNT-1 downto 0 ) of upp_reg ;
-- Type d'un bus d'adresse de registre :
subtype upp_reg_addr_bus is signed( ADDR_BUS_SIZE-1 downto 0 ) ;
-- Type d'un code operation :
type upp_opcode is ( op_null, op_load, op_move, op_add, op_sub,
op_read, op_unsupported ) ;
-- Type d'une instruction :
type upp_instruction is
record
opcode : upp_opcode ;
operande1 : upp_reg_addr_bus ;
operande2 : upp_reg_addr_bus ;
end record ;
-- Type d'un mode de 'driving' d'un bus :
type upp_regdrivingmode is
record
index : upp_reg_addr_bus ;
-- Soit l'index d'un
registre
flag : boolean ; -- Soit vrai si e/s sur bus e/s du
processeur ( prioritaire )
end record ;
-- Type du 'driving' global du processeur :
type upp_overalldrivingmode is
record
accumulator_mode : upp_regdrivingmode ;
operande1_mode : upp_regdrivingmode ;
operande2_mode : upp_regdrivingmode ;
end record ;
end upp_types ;
Projet de VHDL – un mini-processeur
- 12 -
3.3
packages
Nous avons utilisé deux packages dans ce projet :
- un package upp_util_types.vhdl, dont le contenu est ci-dessus. Ce package contient les types et
constantes utilisés dans le projet,
- un package upp_util_tools.vhdl, dans lequel on a placé une fonction utile qui nous permet de
convertir des integer en signed. Dans cette fonction, nous avons simplement « hardcodé » les
correspondances (ex : « 5 » -> « 101 »).
Fichier upp_util_tools.vhdl
1. library ieee ;
use ieee.std_logic_1164.all ;
use ieee.numeric_std.all ;
use work.upp_types.all ;
package upp_tools is
function int2signed ( a : integer ) return signed ;
end upp_tools ;
package body upp_tools is
function int2signed ( a : integer ) return signed is
variable u : signed( ADDR_BUS_SIZE-1 downto 0 ) ;
begin
case a is
when 0 => u:="000" ;
when 1 => u:="001" ;
when 2 => u:="010" ;
when 3 => u:="011" ;
when 4 => u:="100" ;
when 5 => u:="101" ;
when 6 => u:="110" ;
when 7 => u:="111" ;
when others => u:="000" ;
end case ;
return u ;
end function int2signed ;
end package body upp_tools ;
Projet de VHDL – un mini-processeur
- 13 -
4
ARCHITECTURE DU COMPOSANT
Nous avons choisi d’utiliser l’architecture suivante :
Le composant Un Petit Processeur (UPP) est la réunion de 3 modules :
4.1
L’unité arithmétique et logique (upp_alu)
upp_alu est l’unité qui exécute les instructions. Afin de limiter la surface du composant, elle opère
sur deux registres opérandes et place le résultat en sortie sur l’accumulateur. Ainsi, dans le cas
d’une addition par exemple, le résultat de l’addition entre les deux opérandes est placé dans
l’accumulateur en sortie. Afin de n’avoir à utiliser qu’un seul additionneur pour le circuit, nous
Projet de VHDL – un mini-processeur
- 14 -
avons implémenté de la manière suivante : le soustracteur est complémenté à deux, puis placé en
entrée de l’additionneur.
Lorsque le résultat est calculé, upp_alu inverse le signal accumulator_update pour signifier aux
autres composants que la valeur de l’accumulateur a été actualisée.
Entrées :
- operande1, operande2, de type upp_reg, servant de données pour effectuer l’opération,
- opcode, de type upp_opcode, l’opcode de l’instruction a réaliser (ex : op_add, op_move, …).
Sorties :
- s, de type upp_reg, le résultat de l’opération,
- accumulator_update, de type std_logic, inversé lorsque le résultat est disponible.
4.2
Le décodeur d’instructions (upp_decoder)
upp_decoder est le module qui décode les instructions. Lors de chaque front d’horloge,
l’instruction est placée en entrée de ce module, qui va rechercher l’opcode de l’instruction, les
registres mis en cause et le mode de transport à opérer sur les registres. Lorsque l’instruction est
décodée, la valeur du signal operande_update est inversée pour signifier aux autres composants
que les opérations de l’alu et les opérations de transport sur les reigstres peuvent commencer.
Entrées :
- instruction, de type std_logic_vector, l’instruction donnée en entrée du composant principal,
- h, l’horloge, utiliser pour la synchronisation.
Sorties :
- drivingmode, de type upp_overalldrivingmode, le mode de transport à opérer sur les registres,
- opcode, de type upp_opcode, le type d’instruction (op_add, op_sub, …),
- operande_update, de type std_logic, inversé lorsque le décodage est fini.
4.3
Le pilote de registres (upp_regdriver)
upp_regdriver est le composant qui va positionner les valeurs correctes sur les registres
d’opérandes transmis à upp_alu, et qui recopiera la valeur de retour de upp_alu sur l’emplacement
adhoc, en fonction de l’instruction qui aura été effectuée.
C’est aussi dans ce module que les registres de données R0 à Rx seront déclarés. C’est le seul
composant qui peut lire/écrire sur les registres.
Entrées :
- raz, le signal de remise à zéro. Ceci pilote la remise à zéro des registres de données,
Projet de VHDL – un mini-processeur
- 15 -
-
accumulator_update, operande_update, de type std_logic, les signaux venant respectivement
de upp_alu et upp_decoder, utilisés pour synchroniser le repositionnement des registres
accumulateur et opérandes,
- data_in, de type upp_reg, l’entrée de données du composant principal,
- reg_accumulator, de type upp_reg, le registre accumulateur, c’est le registre sur lequel
upp_alu sort le résultat de l’instruction en cours d’exécution,
- mode, de type upp_overalldrivingmode, la structure de données codant pour le mode de
transmission des données entre registres et entrées/sorties du composant principal.
Sorties :
- reg_operande1, reg_operande2, de type upp_reg, les deux registres opérandes utilisés par
upp_alu,
- data_out, de type upp_reg, la sortie de données du composant principal,
- data_valid, de type std_logic, positionné à 1 lorsque la valeur sur data_out est correcte.
Projet de VHDL – un mini-processeur
- 16 -
5
5.1
5.1.1
CODAGE EN VHDL
Outils pour l’aide à la compilation
Script pour le paramétrage des constantes du système
Nous avons mis au point un script Perl qui permet facilement de modifier les constantes spécifiées
dans les fichiers VHDL en modifiant seulement un fichier de constantes.
Le fichier de constantes prend la forme suivante (fichier project.conf):
REG_SIZE=16
REG_COUNT=8
OPCODE_COUNT=8
Ensuite, nous écrivons un fichier de configuration qui définit le modèle à suivre pour le
remplacement des constantes. Ci-dessous, un extrait du fichier upp_util_types.vhdl.conf :
constant
constant
constant
constant
REG_SIZE : integer := $(REG_SIZE);
REG_COUNT : integer := $(REG_COUNT);
ADDR_BUS_SIZE : integer := LOG($(REG_COUNT));
OPCODE_BIT_COUNT : integer := LOG($(OPCODE_COUNT));
Après exécution du script, un fichier upp_util_types.vhdl est généré. En voici un extrait :
constant
constant
constant
constant
REG_SIZE : integer := 16;
REG_COUNT : integer := 8;
ADDR_BUS_SIZE : integer := 3;
OPCODE_BIT_COUNT : integer := 3;
Voici le script Perl que l’on a écrit pour le projet :
#!/usr/esiee/bin/perl
#fichier configure.pl
# Ce fichier permet de remplacer les valeurs specifiees
# dans le fichier de configuration dans le code VHDL
# des fichiers de constantes
sub LoadFile {
local($line);
open(FILE1,$_[0]) || die "Can't open file $_[0]";
$_[1]='';
while($line=<FILE1>) {
Projet de VHDL – un mini-processeur
- 17 -
$_[1].=$line;
}
close(FILE1);
}
$MASTER=$ARGV[0];
$PATTERN=$ARGV[1];
$SLAVE=$ARGV[2];
open(MASTERFILE,"$MASTER") || die "Can't open file $MASTER";
&LoadFile($PATTERN,$pattern);
while (<MASTERFILE>)
{
chop;
if (/=/)
{
$SYMBOL=$`;
$VALUE=$';
$pattern =~ s/\${$SYMBOL}/$VALUE/g;
}
}
while ( $pattern =~ /LOG\((.*)\)/ )
{
$ARGUMENT=$&;
$ARGUMENT =~ s/LOG\(//;
$ARGUMENT =~ s/\)//;
$ARGUMENT =~ s/ //g;
$VALUE=(log($ARGUMENT)/log(2));
$pattern =~ s/LOG\([ ]*$ARGUMENT[ ]*\)/$VALUE/g;
}
$pattern =~ s/LOG\((.*)\)/(\1)/g;
close(MASTERFILE);
open(SLAVEFILE,">$SLAVE") || die "Can't write to $SLAVE";
print SLAVEFILE "$pattern";
close(SLAVEFILE);
Le script s’utilise de la manière suivante :
# ./configure.pl project.conf upp_util_types.vhdl.conf upp_util_types.vhdl
Projet de VHDL – un mini-processeur
- 18 -
L’intérêt d’un script comme celui-ci est qu’il permet de définir un unique fichier de constantes.
D’autre part, on peut facilement procéder à des opérations du type logarithme de base 2, puissance,
etc. Enfin, il est facile à encapsuler dans les commandes d’un fichier makefile.
5.1.2
Fichier Makefile
Pour compiler l’ensemble des fichiers nécessaires au projet, il faut taper un certain nombre de
lignes de commandes, regroupant :
- la ligne de commandes qui fait appel au script Perl chargé de traiter les constantes du système,
notées dans le fichier project.conf,
- les lignes de commandes pour la compilation des fichiers VHDL. Cet ensemble de lignes de
commandes dépend de l’état du système : par exemple, si l’on ne modifie que le fichier
contenant l’architecture du composant principal, il n’est pas nécessaire de recompiler les autres
fichiers. Si l’on modifie le package de constantes et de types, il faut tout recompiler. Si l’on
modifie seulement un sous-composant, il ne faut recompiler que le sous-composant et le
composant principal. On a ainsi des dépendances entre les fichiers sources, parfaitement gérées
par un fichier d’informations sur la compilation de projets du type Makefile.
5.2
Entités utilisées
5.2.1
Upp
5.2.2
Upp_alu
entity upp_alu_entity is
port ( operande1, operande2 : in upp_reg;
opcode : in upp_opcode;
s : out upp_reg;
);
end upp_alu_entity;
5.2.3
Upp_decoder
entity upp_decoder_entity is
port ( instruction: in std_logic_vector(INSTRUCTION_SIZE-1 downto 0);
drivingmode: out upp_overalldrivingmode;
opcode: out upp_opcode
);
Projet de VHDL – un mini-processeur
- 19 -
end upp_decoder_entity;
5.2.4
Upp_regdriver
entity upp_regdriver_entity is
port ( raz : in std_logic;
reg_accumulator : in upp_reg;
data_in : in upp_reg;
mode: in upp_overalldrivingmode;
reg_operande1 : out upp_reg;
reg_operande2 : out upp_reg;
data_out: out upp_reg
);
end upp_regdriver_entity;
5.3
5.3.1
Sources VHDL des composants modélisés
Upp.vhdl
----------- ESIEE I4 --------------- EL411 : Projet de VHDL
-- Un Petit Processeur ( upp )
-- Gregory HEINRICH / Mael LE BERRE
--------------------------------------- upp :
-- Un Petit Processeur
-- Suivant le cahier des charge fourni pour le projet.
library ieee ;
use ieee.std_logic_1164.all, ieee.numeric_std.all ;
library work ;
use work.upp_types.all ;
entity upp_entity is
port ( enable, h, raz
: in std_logic ;
data_in: in std_logic_vector ( REG_SIZE-1 downto 0 ) ;
instruction: in std_logic_vector ( INSTRUCTION_SIZE-1 downto 0 )
;
data_out: out std_logic_vector ( REG_SIZE-1 downto 0 ) ;
data_valid: out std_logic
) ;
Projet de VHDL – un mini-processeur
- 20 -
end upp_entity ;
architecture upp_arch of upp_entity is
signal instruction_reg : std_logic_vector ( INSTRUCTION_SIZE-1 downto 0 ) := (
others => '0' ) ; -- Registre contenant l'instruction en cours de traitement. (
utilise par 'decoder' )
signal drivingmode : upp_overalldrivingmode ; -- Ligne de 'driving' des bus. (
entre 'decoder' et 'regdriver' )
signal opcode_reg : upp_opcode ; -- Code operation de l'instruction en cours. (
utilise par 'alu' )
signal operande1_bus, operande2_bus, accumulator_bus : upp_reg ; -- Les bus
principaux. ( entre 'alu' et 'regdriver' )
signal data_out_signal : signed( REG_SIZE-1 downto 0 ) := ( others => '0' ) ; - Le signal data_out.
signal accumulator_update,operande_update : std_logic ; -- Les signaux de mise
a jour des bus.
begin
-- Decoder : Decode l'instruction pour en extraire l'opcode et les
modes de 'driving' des bus.
decoder : entity work.upp_decoder_entity( upp_decoder_arch ) port map(
instruction_reg, h, drivingmode, opcode_reg,operande_update ) ;
-- Alu : Organe de calcule central.
alu :
entity work.upp_alu_entity( upp_alu_arch ) port map(
operande1_bus, operande2_bus, opcode_reg, accumulator_bus, accumulator_update )
;
-- Regdriver : Organe qui oriente les donnee entre les bus, les
registres et les e/s. ( contient les registres )
regdriver: entity work.upp_regdriver_entity( upp_regdriver_arch ) port
map( raz,accumulator_update,operande_update,accumulator_bus, signed( data_in ),
drivingmode, operande1_bus, operande2_bus, data_out_signal,data_valid ) ;
data_out <= std_logic_vector ( data_out_signal ) ;
process ( raz,H ) -- Chargement de l'instruction a chaque coup
d'horloge :
begin
if ( raz='0' ) or ( enable='0' ) then
instruction_reg <= ( others => '0' ) ;
elsif ( rising_edge( H ) ) then
instruction_reg <= instruction ;
end if ;
end process ;
end architecture upp_arch ;
Projet de VHDL – un mini-processeur
- 21 -
5.3.2
Upp_alu.vhdl
----------- ESIEE I4 --------------- EL411 : Projet de VHDL
-- Un Petit Processeur ( upp )
-- Gregory HEINRICH / Mael LE BERRE
--------------------------------------- upp_alu :
-- Unite Arithmetique et Logique :
-- Realise une fonction combinatoire entre 2 operandes. ( addition,
soustraction ou simple copie. )
-- Ecrit le resultat dans l'accumulateur.
library ieee ;
use ieee.std_logic_1164.all, ieee.numeric_std.all ;
library work ;
use work.upp_types.all ;
entity upp_alu_entity is
port (
operande1, operande2 : in upp_reg ; -- Les 2 operandes.
opcode : in upp_opcode ; -- Le code operation a realiser.
accumulator : out upp_reg := ( others => '0' ) ; -L'accumulateur.
accumulator_update : out std_logic -- signal de mise a jour de
l'accumulateur.
) ;
end upp_alu_entity ;
architecture upp_alu_arch of upp_alu_entity is
signal accumulator_update_intern : std_logic:='0' ;
signal accumulator_intern : upp_reg := ( others => '0' ) ;
-- signal intermmediaire resultat de l'operation.
signal operande2_intern : upp_reg ;
-- operande2 eventuellement
complemente a 2 dans le cas d'une soustraction.
begin
process ( operande1, operande2_intern, opcode )
begin -- Process de calcul principale : realise une addition ou une
simple copie.
case opcode is
when op_add|op_sub
=> accumulator_intern <= (
operande1 + operande2_intern ) ;
when op_move|op_load|op_read => accumulator_intern <=
operande1 ;
when others => accumulator_intern <= ( others => '0' )
; -- pour les operation non suportée : on met 0 dans l'accumulateur.
end case ;
end process ;
process ( accumulator_intern )
begin -- Process de mise a jour : Si l'accumulateur change, on le met a
Projet de VHDL – un mini-processeur
- 22 -
jour et on transmet l'information par le signal de mise a jour. :
accumulator <= accumulator_intern ;
accumulator_update_intern <= not accumulator_update_intern ;
accumulator_update <= accumulator_update_intern ;
end process ;
with opcode select -- Dans le cas d'une soustraction, operande 2 est
complemente a 2.
operande2_intern <=
( not operande2 ) + 1 when op_sub, -- complement a 2.
operande2 when others ;
end architecture upp_alu_arch ;
5.3.3
Upp_decoder.vhdl
-- ----------- ESIEE I4 --------------- EL411 : Projet de VHDL
-- Un Petit Processeur ( upp )
-- Gregory HEINRICH / Mael LE BERRE
--------------------------------------- upp_decoder :
-- decodeur d'instruction :
-- extrait de l'instruction :
-- Le code operation.
-- La maniere de 'driver' les bus.
library ieee ;
use ieee.std_logic_1164.all, ieee.numeric_std.all ;
library work ;
use work.upp_types.all ;
entity upp_decoder_entity is
port (
instruction: in std_logic_vector ( INSTRUCTION_SIZE-1 downto 0
) ;
-- Instruction.
h: in std_logic ; -- Horloge.
drivingmode: out upp_overalldrivingmode ; -- mode de 'driving'
: cf upp.
opcode: out upp_opcode ; -- code operation.
operande_update : out std_logic -- signal de mise a jour des
operandes.
) ;
end upp_decoder_entity ;
architecture upp_decoder_arch of upp_decoder_entity is
signal opcode_intern : upp_opcode ;
-- Signal de code operation interne :
reutilise pour le 'driving'.
signal drivingmode_intern : upp_overalldrivingmode ;
-- signal de 'driving'
interne : reutilise pour la mise a jour des operandes.
Projet de VHDL – un mini-processeur
- 23 -
signal operande_update_intern,operande_update_intern1,operande_update_intern2 :
std_logic := '0' ; -- Signaux de remise a jour.
begin
with instruction( instruction'length-1 downto instruction'length OPCODE_BIT_COUNT ) select
opcode_intern <= -- Description des opcodes :
op_null when "000",
op_load when "001",
op_move when "010",
op_add
when "011",
op_sub
when "100",
op_read when "101",
op_unsupported when others ;
opcode <= opcode_intern ;
-- GESTION DES REGISTRES
-- les premiers bits de l'instructions apres le code operation
indiquent le registre
-- source et/ou destination de toute operation sur un registre.
-- ils sont donc les bits qui vont 'driver' l'operande1 et
l'accumulateur avec les registres :
drivingmode_intern.accumulator_mode.index <= signed( instruction(
instruction'length-OPCODE_BIT_COUNT-1 downto ADDR_BUS_SIZE ) ) ;
drivingmode_intern.operande1_mode.index <= signed( instruction(
instruction'length-OPCODE_BIT_COUNT-1 downto ADDR_BUS_SIZE ) ) ;
-- Les bits suivants indiquent le registre source du deuxieme operande
quand il est necessaire.
-- ils sont donc les bits qui vont 'driver' l'operande2 avec les
registres :
drivingmode_intern.operande2_mode.index <= signed( instruction(
ADDR_BUS_SIZE-1 downto 0 ) ) ;
-- le deuxieme operande n'est jamais charge par data_in donc son flag
est toujours a zero :
drivingmode_intern.operande2_mode.flag <= false ;
-- GESTION ENTREE/SORTIE
-- Dans la partie precedente, le 'driving' des registres n'a pas tenu compte
-- du code operation : l'information de 'driving' avec les registres est
systematique,
-- mais ne sera pas toujours pris en compte car le 'flag' d'entree/sortie est
considere
-- comme prioritaire.
with opcode_intern select -- l'accumulateur est dirige vers la sortie (
data_out ) pour l'operation 'op_read' :
drivingmode_intern.accumulator_mode.flag <=
true when op_read,
false when others ;
with opcode_intern select -- l'entree ( data_in ) est dirigee vers
l'operateur 1 pour l'operation 'op_load' :
drivingmode_intern.operande1_mode.flag <=
Projet de VHDL – un mini-processeur
- 24 -
true when op_load,
false when others ;
process( drivingmode_intern )
begin -- A chaque modification de 'drivingmode' : les operandes doivent
etre recharges :
operande_update_intern1 <= not operande_update_intern1 ;
end process ;
process( h )
begin -- A chaque coup d'orloge, les operandes doivent etre recharges :
-- ( pour le cas ou une meme instruction serait repetee plusieurs fois
auquel cas 'drivingmode' n'est pas modifié. )
if rising_edge( h ) then
operande_update_intern2 <= not operande_update_intern2
;
end if ;
end process ;
-- la mise a jours des operandes dependant de 2 conditions, on realise
un xor entre celles-ci
-- pour prendre en compte la variation de chacune.
operande_update_intern <= operande_update_intern1 xor
operande_update_intern2 ;
process( operande_update_intern )
begin -- Chaque modification entraine le rechargement des sorties de
'driving'.
drivingmode <= drivingmode_intern ;
operande_update <= operande_update_intern ;
end process ;
end ;
-- architecture upp_decoder_arch ;
5.3.4
Upp_regdriver.vhdl
----------- ESIEE I4 --------------- EL411 : Projet de VHDL
-- Un Petit Processeur ( upp )
-- Gregory HEINRICH / Mael LE BERRE
--------------------------------------- upp_regdriver :
-- Contient l'instance des registres et 'Drive' les registres avec les bus :
-- Recoit le mode de 'driving' du decodeur d'instruction.
-- Charge les operandes ( d'un registre ou de data_in ).
-- Copie l'accumulateur la ou il doit etre copie ( dans un registre ou
dans data_out ).
-- Se charge d'emettre le signal data_valid.
Projet de VHDL – un mini-processeur
- 25 -
library ieee ;
use ieee.std_logic_1164.all, ieee.numeric_std.all ;
library work ;
use work.upp_types.all ;
use work.upp_tools.all ;
entity upp_regdriver_entity is
port (
raz : in std_logic ;
-- Remise a zero generale.
accumulator_update,operande_update : in std_logic ; -- signaux
de mise a jour des bus.
reg_accumulator : in upp_reg ; -- Le bus accumulateur.
data_in : in upp_reg ;
mode: in upp_overalldrivingmode ; -- Le mode de 'driving'.
reg_operande1 : out upp_reg ; -- Les bus operande.
reg_operande2 : out upp_reg ;
data_out: out upp_reg := ( others => '0' ) ;
data_valid : out std_logic := '0'
) ;
end upp_regdriver_entity ;
architecture upp_regdriver_arch of upp_regdriver_entity is
-- Instance des registres :
signal reg_bank : upp_reg_array := ( others =>( others => '0' ) ) ;
begin
process( mode.accumulator_mode,accumulator_update,raz )
begin -- Process gerant l'ecriture dans les registres.
-- Sensibilites :
-- si l'accumulateur change ( accumulator_update ),
-- son mode de 'driving'( mode.accumulator_mode )
-- ou enfin en cas de remise a zero ( raz ).
if ( raz = '0' ) then -- Si raz = 0 : on remet tous les
registres a zero :
for i in 0 to REG_COUNT-1 loop
reg_bank( i ) <=( others => '0' ) ;
end loop ;
data_out <= ( others => '0' ) ; -- data _out et
data_valid aussi.
data_valid <= '0' ;
else
if ( mode.accumulator_mode.flag=false ) then
data_valid <= '0' ; -- si l'accumulateur doit
etre ecrit dans un registre : data_valid = 0
-- Dans ce cas : chaque registre recoit sa
valeur si son index
-- est egale a celui indique dans le mode de
driving :
Projet de VHDL – un mini-processeur
- 26 -
for i in 0 to REG_COUNT-1 loop
if ( mode.accumulator_mode.index =
int2signed( i ) ) then
reg_bank( i ) <=
reg_accumulator ;
end if ;
end loop ;
else -- si l'accumulateur doit etre ecrit dans data_out
:
data_out <= reg_accumulator ; -- On l'y ecrit.
data_valid <= '1' ; -- On met data_valid a 1.
end if ;
end if ;
end process ;
process( data_in,operande_update,mode.operande1_mode )
begin -- Process gerant la lecture dans les registres.
-- Sensibilites :
-- si data_in change ( data_in ),
-- ou si le decodeur demande la mse a jour des operandes. (
operande_update )
if ( raz = '1' ) then
if ( mode.operande1_mode.flag=false ) then
-- si un registre doit etre ecrit dans l'operande1 :
-- chaque registre ecrit sa valeur si son index
-- est egale a celui indique dans le mode de driving :
for i in 0 to REG_COUNT-1 loop
if ( mode.operande1_mode.index =
int2signed( i ) ) then
reg_operande1 <= reg_bank( i )
;
end if ;
end loop ;
else
-- si data_in doit etre ecrit dans operande1 :
reg_operande1 <= data_in ;
end if ;
end if ;
end process ;
process( data_in,operande_update )
begin -- Process gerant la lecture dans les registres.
-- Sensibilites :
-- si data_in change ( data_in ),
-- ou si le decodeur demande la mse a jour des operandes. (
operande_update )
if ( raz = '1' ) then
if ( mode.operande2_mode.flag=false ) then
-- si un registre doit etre ecrit dans l'operande1 :
-- chaque registre ecrit sa valeur si son index
-- est egale a celui indique dans le mode de driving :
for i in 0 to REG_COUNT-1 loop
Projet de VHDL – un mini-processeur
- 27 -
if (
mode.operande2_mode.index=int2signed( i ) ) then
reg_operande2 <= reg_bank( i )
;
end if ;
end loop ;
else
-- si data_in doit etre ecrit dans operande1 :
reg_operande2 <= data_in ;
end if ;
end if ;
end process ;
end ; -- architecture upp_regdriver_arch;
Projet de VHDL – un mini-processeur
- 28 -
6
SIMULATIONS
6.1
Simulation du circuit de décodage (upp_decoder)
6.2
Simulation du circuit d’opérations logiques et arithmétiques (upp_alu)
Projet de VHDL – un mini-processeur
- 29 -
6.3
Simulation du pilote de registres (upp_regdriver)
Projet de VHDL – un mini-processeur
- 30 -
6.4
Simulation du composant principal upp
Projet de VHDL – un mini-processeur
- 31 -
7
SYNTHESE
Un des impératifs du cahier des charges était d’obtenir un circuit synthétisable. Nous allons voir
dans les sections suivantes pour chaque composant comment les circuits peuvent être synthétisés,
et quelles en sont les caractéristiques (surface, …).
Projet de VHDL – un mini-processeur
- 32 -
7.1
Upp_alu
Projet de VHDL – un mini-processeur
- 33 -
7.2
Upp_decoder
Projet de VHDL – un mini-processeur
- 34 -
7.3
Upp_regdriver
Projet de VHDL – un mini-processeur
- 35 -
7.4
Upp
7.4.1

Upp, avec sa hiérarchie de composants conservée
Surface avant dissolution de la hiérarchie :
Module
upp_entity
upp_alu_entity
upp_decoder_entity
upp_regdriver_entity
AWDP_ADD_partition_0
AWDP_ADD_partition_1
7.4.2
Wireload
B0X0
B0X0
B0X0
B0X0
B0X0
B0X0
Cell Area
1809.00
329.00
8.00
1398.00
123.00
74.00
Net Area
0.00
0.00
0.00
0.00
0.00
0.00
Total Area
1809.00
329.00
8.00
1398.00
123.00
74.00
Cell Area
287.00
Net Area
0.00
Total Area
287.00
Upp, hiérarchie dissoute
7.4.2.1 Caractéristiques
 Surface après dissolution de la hiérarchie :
Module
upp_entity
Wireload
not mapped
Projet de VHDL – un mini-processeur
- 36 -
7.4.2.2 Schéma
Projet de VHDL – un mini-processeur
- 37 -
8
POUR ALLER PLUS LOIN…
Comment aurait-on pu aller plus loin dans ce projet ? Les microprocesseurs d’aujourd’hui sont des
machines constituées de dizaines de millions de transistors, toujours plus rapides et complexes.
Voici quelques unes des améliorations que l’on pourrait apporter à notre upp. La plupart d’entre
elles nécessiteraient une refonte radicale de notre architecture, ainsi que des milliers de
jours*homme supplémentaires sur le projet, mais toutes sont « standard » dans les processeurs de
nos jours :
8.1 Program Counter
Dans notre architecture, on voit bien qu’il est impossible de rajouter des instructions de
branchement vers une autre instruction. Ceci signifie donc qu’il n’est pas possible avec upp
d’exécuter des algorithmes impliquant des prises de décision du type si… alors… sinon.
Avec un Program Counter qui procède à un fetch des instructions dans la mémoire, il serait
possible de simuler des prises de décision.
Un Program Counter impliquerait d’ajouter un circuit supplémentaire pour la gestion/protection de
la mémoire (MMU/MPU),
8.2 Mémoire cache
Ajouter de la mémoire cache à un processeur n’est pas en soi une tâche compliquée (bien qu’elle
soit coûteuse en termes de place sur le weafer de silicone). Ce qui ensuite complique la logique
d’un processeur, c’est ensuite les méthodes utilisées pour :
- choisir quelle section de mémoire sera en cache (une méthode typiquement utilisée est celle du
Round Robin, où la stratégie est semblable à une pile de données,
- déterminer si une zone de mémoire cachée a été modifiée depuis sa mise en cache. Dans la
pratique, on distingue alors souvent le cache d’instructions (censées ne jamais être altérées) et
la cache de données (qui peuvent être modifiées en cours d’exécution), pour lequel il faut
implémenter des méthodes du type WriteBack, WriteThrough, etc.
Lorsqu’une boucle tient entièrement dans le cache processeur, l’exécution de celle-ci peut être 20 à
25 fois plus rapides que s’il faut recupérer les instructions de la mémoire RAM.
8.3 Pipeline
L’idée du Pipeline est celle de la chaîne de montage d’une automobile : plutôt que de faire les
voitures une par une, on décompose le montage en étapes et lorsqu’une étape est accomplie, la
voiture passe à l’étape suivante et une autre la remplace.
Projet de VHDL – un mini-processeur
- 38 -
Ici, on décompose le traitement d’une instruction en étapes, de manière à simplifier chacune des
étapes et être à même d’augmenter la cadence de l’horloge du processeur. Le temps de traitement
des instructions est multiplié par le nombre d’étapes, mais dans la pratique, à chaque cycle
d’horloge (ou presque), une instruction est effectuée.
Ceci rend le processeur beaucoup plus complexe, car en plus de la synchronisation entre les étapes
de traitement (typiquement au minimum fetch, decode, execute), il faut tenir compte des cas où une
donnée utilisée dans le pipeline est modifiée à l’instruction précédente, ainsi que des instructions
de branchement pour lesquelles il faut vider le pipeline pour sauter à l’instruction suivante (sauf
dans le cas où les prédictions de branchement ont permis de « prédire » l’instruction suivante).
Projet de VHDL – un mini-processeur
- 39 -
Téléchargement