Cours:TP printempsM4209
Sommaire
Introduction
Comme nous utilisons la carte DE2-115, le FPGA utilisé est un Cyclone IVE de référence EP4CE115F29C7.
Le compteur de passages est évoqué dans un autre livre : Compteur de passages revu et corrigé. Il sera le fil conducteur de ce TP.
Nous allons, dans cette série de TPs, d'abord implanter complètement un compteur de passages sous forme de fonctions logiques exprimées en langage VHDL, donc sans processeur. Ensuite nous allons implanter dans le FPGA le même compteur de passage, mais réalisé via l'exécution d'un programme C par un processeur seul, sans périphérique matériel particulier. Pour terminer cette série, nous allons d'abord déporter le transcodage, hors du processeur pour le réaliser par des fonctions logiques décrites en VHDL. Puis pour finir, nous déporterons l'ensemble compteurs BCD et transcodeurs vers la réalisation matérielle via le langage VHDL.
L'objectif de ce travail est donc, pour des exemples relativement simples, de bien comprendre les relations entre la partie matérielle (description de fonctions par le langage VHDL) et la partie logicielle ( réalisation de fonctions par l'exécution d'un programme C par un processeur), lorsque ces 2 parties concourent à l'implantation de l'application complète. Cette coopération du matérielle et du logiciel pour une application s'appelle le co-design.
TP 1 (1h30)
Exercice 1
On vous demande simplement de réaliser un transcodeur pour afficher tout nombre sur 4 bits (entrée) sur un afficheur 7 segments.
Indication 1
Vous devez réaliser l'architecture correspondante pour l'entité VHDL suivante :
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
-- zero pour allumer les LEDs
entity transcod7segs is
port (
I_in4 : in std_logic_vector(3 downto 0);
-- Ordre : gfedcba
Q_7segs : out std_logic_vector(6 downto 0)
);
end transcod7segs;
Si le nom des entrées sorties ne vous convient pas, changez-les et assumez ces changements lors des nombreux câblages de ce transcodeurs. Tous nos schémas gardent cette convention de noms, à commencer par le fichier de contraintes que nous donnons maintenant.
Indication 2
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved Q_7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Q_7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Q_7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Q_7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Q_7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Q_7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Q_7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, I_in4[3],Input,PIN_AD27,5,B5_N2,2.5 V, I_in4[2],Input,PIN_AC27,5,B5_N2,2.5 V, I_in4[1],Input,PIN_AC28,5,B5_N2,2.5 V, I_in4[0],Input,PIN_AB28,5,B5_N1,2.5 V,
L'enseignant vous expliquera comment trouver ces contraintes avec la documentation de la carte.
Exercice 2
Réaliser la même chose avec deux afficheurs. On aura donc maintenant 8 entrées et 14 sorties. On vous demande cette réalisation avec deux composants correspondant à l'exercice 1 (transcodeurs)
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved Unit7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Unit7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Unit7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Unit7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Unit7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Unit7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Unit7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V, Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V, Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V, Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V, Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V, Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V, Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V, sw[7],Input,PIN_AB26,5,B5_N1,2.5 V, sw[6],Input,PIN_AD26,5,B5_N2,2.5 V, sw[5],Input,PIN_AC26,5,B5_N2,2.5 V, sw[4],Input,PIN_AB27,5,B5_N1,2.5 V, sw[3],Input,PIN_AD27,5,B5_N2,2.5 V, sw[2],Input,PIN_AC27,5,B5_N2,2.5 V, sw[1],Input,PIN_AC28,5,B5_N2,2.5 V, sw[0],Input,PIN_AB28,5,B5_N1,2.5 V,
TP 2 (1h30)
Exercice 1
En vous aidant du compteur décompteur décimal présenté ici, on vous demande de compléter le code pour faire un compteur décimal cascadable.
Exercice 2
Le compteur cascadable de l'exercice 1 doit être testé. Pour cela vous devez lui envoyer une horloge lente. Vous allez donc assembler :
- un compteur destiné à réaliser une horloge lente (division par 2**24)
- deux compteurs décompteurs décimaux cascadés
- deux transcodeurs
Le fichier de contraintes sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved clk,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, Init,Input,PIN_Y23,5,B5_N2,2.5 V, Aff7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Aff7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Aff7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Aff7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Aff7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Aff7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Aff7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V, Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V, Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V, Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V, Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V, Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V, Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V, DownUp,Input,PIN_AC28,5,B5_N2,2.5 V, en,Input,PIN_AB28,5,B5_N1,2.5 V,
Ce fichier de contraintes correspond à "en" interrupteur complètement à droite, "DownUp" juste à côté et "Init" complètement à gauche.
TP 3 (1h30)
Exercice 1
Réaliser le séquenceur en complétant le code correspondant trouvé ICI dans un autre livre. Vous allez ensuite assembler :
- un compteur destiné à réaliser une horloge lente (division par 2**20) : ce sera l'horloge générale
- un séquenceur
- deux compteurs décompteurs décimaux cascadés
- deux transcodeurs
Le fichier de contrainte sera :
To,Direction,Location,I/O Bank,VREF Group,I/O Standard,Reserved clk,Input,PIN_Y2,2,B2_N0,3.3-V LVTTL, Rst,Input,PIN_Y23,5,B5_N2,2.5 V, Aff7segs[6],Output,PIN_H22,6,B6_N0,2.5 V, Aff7segs[5],Output,PIN_J22,6,B6_N0,2.5 V, Aff7segs[4],Output,PIN_L25,6,B6_N1,2.5 V, Aff7segs[3],Output,PIN_L26,6,B6_N1,2.5 V, Aff7segs[2],Output,PIN_E17,7,B7_N2,2.5 V, Aff7segs[1],Output,PIN_F22,7,B7_N0,2.5 V, Aff7segs[0],Output,PIN_G18,7,B7_N2,2.5 V, Diz7segs[6],Output,PIN_U24,5,B5_N0,2.5 V, Diz7segs[5],Output,PIN_U23,5,B5_N1,2.5 V, Diz7segs[4],Output,PIN_W25,5,B5_N1,2.5 V, Diz7segs[3],Output,PIN_W22,5,B5_N0,2.5 V, Diz7segs[2],Output,PIN_W21,5,B5_N1,2.5 V, Diz7segs[1],Output,PIN_Y22,5,B5_N0,2.5 V, Diz7segs[0],Output,PIN_M24,6,B6_N2,2.5 V, CapteurGauche,Input,PIN_AC28,5,B5_N2,2.5 V, CapteurDroit,Input,PIN_AB28,5,B5_N1,2.5 V,
CapteurDroit est l'interrupteur complètement à droite, CapteurGauche est juste à côté tandis que Rst est complètement à gauche.
Voici en schéma ce que vous devez réaliser.
Remarque : Notez que ce séquenceur suppose qu'il n'est pas possible d'avoir en même temps capteurGauche et capteurDroit. Si ce n'est pas le cas, il serait bon d'ajouter un état sur chaque branche.
Exercice 2
Changer le séquenceur pour laisser la possibilité de réaliser un demi-tour.
TP 4 (1h30)
Vous allez maintenant réaliser vos premiers programmes en C destinés à un processeur dans le FPGA. Le code source complet du processeur vous est donné. Vous allez donc réaliser un projet pour compiler ce processeur. Cette compilation peut être assez longue (près de 10 mn) mais la simple mise en RAM/ROM du programme ne prendra que 2 mn une fois cette première compilation réalisée.
Voici sous forme schématique l'ensemble du processeur.
Ressources
Andreas Hilvarsson a écrit ce cœur ATTiny861 et l'a publié chez Opencores.org. Nous avons modifier sa façon d'y créer des périphériques et en particulier retiré tous les PORTs bidirectionnels qui ne servent à rien dans un FPGA.
Remarque : Nous mettons la ressource complète sur notre site : M4209TinyStart.zip.
Pour information
Pour information la réalisation des périphériques en sortie a été réalisée comme dans la figure ci-contre. Rappelez-vous que la réalisation de périphérique de sortie se fait dans le fichier microcontroleur.vhd et dans le process iowr... et que c'est ce qui est montré dans cette figure.
Exercices
Voici un exemple de programme C :
#include "avr/io.h"
#undef F_CPU
#define F_CPU 15000000UL
#include "util/delay.h"
//#define UCSRB _SFR_IO8(0x01)
//#define UCSRA _SFR_IO8(0x02)
//#define UDR _SFR_IO8(0x03)
// UCSRA
//#define RXC 7
//#define TXC 6
//#define UDRE 5
//UCSRB
//#define RXEN 4
//#define TXEN 3
//***********************************************************************
// main
//***********************************************************************
int main (void) {
unsigned char ch=128;
while(1) {
// echo simple
PORTA = ch;
ch >>= 1;
if (ch == 0) ch = 128;
_delay_ms(300); // on défiler les valeurs
}
return 0;
}
Repérez dans ce code comment ne pas aller trop vite, comment décaler une valeur, comment sortir sur les leds....
Exercice 1
Vous allez réaliser divers chenillards dans un premier temps :
- chenillard gauche droite (très facile non ?)
- chenillard droite gauche
- chenillard droite et gauche simultanés
- chenillard à accumulation
Exercice 2
On vous donne deux sous-programmes facilitant cet exercice :
void incrementBCD(unsigned char *cnt) {
(*cnt)++;
if ((*cnt & 0x0F) > 0x09) *cnt += 6;
if ((*cnt & 0xF0) > 0x90) *cnt = 0;
}
void decrementBCD(unsigned char *cnt) {
(*cnt)--;
if ((*cnt & 0x0F) == 0x0F) *cnt -= 6;
if ((*cnt & 0xF0) == 0xF0) *cnt = 0x99;
}
ainsi qu'un tableau de valeurs précalculées
const unsigned char digit7segs[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
Vous allez réaliser divers compteurs et utiliser le ou les afficheurs sept segments.
- compteur binaire assez lent pour que l'affichage se fasse correctement en hexadécimal sur deux digits
- compteur décimal sur deux digits
Notez que c'est à vous, programmeur, de réaliser le transcodage. Un moyen simple de faire cela est d'utiliser un tableau. Ce n'est peut être pas un hasard si l'on vous a donné un tableau.
Exercice 3
Réaliser le compteur de passages. On vous demande de repérer dans le code ci-dessous la réalisation du séquenceur.
#include "avr/io.h"
#undef F_CPU
#define F_CPU 15000000UL
#include "util/delay.h"
//#define UCSRB _SFR_IO8(0x01)
//#define UCSRA _SFR_IO8(0x02)
//#define UDR _SFR_IO8(0x03)
// UCSRA
//#define RXC 7
//#define TXC 6
//#define UDRE 5
//UCSRB
//#define RXEN 4
//#define TXEN 3
//const unsigned char digit7segs[16]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E};
void incrementBCD(unsigned char *cnt);
void decrementBCD(unsigned char *cnt);
//***********************************************************************
// main
//***********************************************************************
int main (void) {
//unsigned char transcod7segs[]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0xFF,0x00};
unsigned char transcod7segs[]={0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
unsigned char cmpt=0,ch=128,swPresent=0,swPasse=0,etat=1;
while(1) {
// compteur simple
PORTB = transcod7segs[cmpt&0x0F];
DDRB = transcod7segs[(cmpt&0xF0)>>4];
swPresent = PINA;
switch (etat) {
case 0x01 : if (((swPresent & 0x03)==0x01) && ((swPasse & 0x01)==0x00)) {etat = 0x02;break;}
if (((swPresent & 0x03)==0x02) && ((swPasse & 0x02)==0x00)) {etat = 0x08;break;}
case 0x02 : if (((swPresent & 0x03)==0x02) && ((swPasse & 0x02)==0x00)) etat = 0x04;break;
case 0x04 : etat = 0x01; break;
case 0x08 : if (((swPresent & 0x03)==0x01) && ((swPasse & 0x01)==0x00)) etat = 0x10;break;
case 0x10 : etat = 0x01; break;
default : etat = 0x01;
}
if (etat==0x04) incrementBCD(&cmpt);
if (etat==0x10) decrementBCD(&cmpt);
swPasse = swPresent;
PORTA = ch;
ch >>= 1;
if (ch == 0) ch = 128;
_delay_ms(300); // on verra passer les caractères
}
return 0;
}
void incrementBCD(unsigned char *cnt) {
(*cnt)++;
if ((*cnt & 0x0F) > 0x09) *cnt += 6;
if ((*cnt & 0xF0) > 0x90) *cnt = 0;
}
void decrementBCD(unsigned char *cnt) {
(*cnt)--;
if ((*cnt & 0x0F) == 0x0F) *cnt -= 6;
if ((*cnt & 0xF0) == 0xF0) *cnt = 0x99;
}
Modifier ce code pour être plus conforme au séquenceur du TP3 (7 états contre 5 ici).
TP 5 (1h30)
Nous allons déporter les transcodeurs maintenant comme le montre la figure ci-contre.
Vous devez remarquer sur cette figure que maintenant le PORTB (8 bits) permet de commander deux afficheurs sept segments. Votre seul problème maintenant est de réaliser un compteur BCD sur 8 bits et de sortir sa valeur dans PORTB.
Exercice 1
Réaliser la partie matérielle dans microcontroleur.vhd en ajoutant les deux transcodeurs.
Exercice 2
Réaliser un compteur BCD qui compte ou décompte en fonction d'une entrée sur un switch. Evidemment ce que l'on vous demande de faire ici, c'est un programme C qui sera compilé et chargé dans le processeur.
Exercice 3
Refaire le compteur de passages correctement. Inspirez-vous de ce que vous venez de faire dans l'exercice précédent et du compteur de passages du TP précédent.
TP 6 (1h30)
Nous arrivons maintenant au terme de notre série de TPs. Notre objectif maintenant est d'ajouter les deux compteurs BCD comme périphériques. Ils attaqueront les deux transcodeurs qui seront chargés de l'affichage.
A partir de maintenant, fini les calculs d'additions et de soustractions BCD en C. C'est le matériel qui fera tout.
Il y a plusieurs possibilités pour réaliser cela. Nous avons choisi de réaliser cela de la manière suivante :
- le processeur est incapable de mettre une valeur particulière dans les deux compteurs BCD
- pour incrémenter le processeur écrit '1' dans le PORTB
- pour décrémenter, le processeur écrit '0' dans le PORTB
Exercice 1
- Réaliser la partie matérielle correspondant à la figure.
- Modifier et tester le programme du compteur de passages
Remarque : La compréhension de la présence de la bascule D n'est pas forcément très facile à appréhender. Elle est liée au fait que le bit de poids faible de PORTB sert à changer l'entrée UD (choix comptage ou décomptage) des deux compteurs. Si en même temps on valide avec 'en' et bien on aura toujours un temps de retard. En retardant, on positionne d'abord 'UD' puis on valide le comptage ou le décomptage.
Exercice 2
Modifier, pour terminer, la partie matérielle pour que la lecture par le processeur des deux compteurs BCD soit possible. Réaliser maintenant un programme pour tester. Une idée possible est d'afficher la valeur lue sur les LEDs du PORTA qui ne sont pas utilisées.