PulseGenerator : Developpement cote CPLD

De Wiki_du_Réseau_des_Electroniciens_du_CNRS
Aller à la navigationAller à la recherche

<maintab>AccueilProjet Pulse Generator</maintab>
<subtab>PrésentationCahier des chargesGestion du projetCôté PCCôté PICCôté CPLDMembres</subtab>

Le projet "Pulse Generator" a permis de donner naissance à un groupe de travail autour des technologies FPGA/CPLD.

Nous vous invitons à contacter l'animateur par mail pour savoir comment y participer. Merci à lui pour son travail et ses efforts ;-)

Pages crées à cette occasion

  • Organisation du SVN
  • Premiers pas
  • Tutoriel
  • Guides
  • Projets

Les projets de test

Vous trouverez à l'adresse suivante les descriptifs des programmes de test préliminaires écrits pour valider les fonctionnements du SPI et du générateur de pulse et qui ont permis d'orienter le développement de cette version du code.

Projet Pulse Generator

Dans le cadre du projet Pulse Generator, dont le prototype prévoit l'utilisation d'une carte CPLD de type Coolrunner-II, un design VHDL a été crée.

Les différentes parties du design sont décrites ci-dessous.

Il convient de rappeler que chacun est libre d'apporter les modifications qu'il souhaite pour sa propre application et qu'il est tout à fait possible de réutiliser ce projet pour l'adapter sur d'autres plateformes que celle décrite ci-dessous.

TOP de l'architecture

Fichiers concernés :

  • PulseGenerator_Top.vhd
  • PulseGenerator_Top_TB.vhd : pour la simulation
  • PulseGenerator_Top_TB.wcfg : pour la configuration des signaux pour la simulation

Le fichier TOP instancie l'ensemble des modules. L'architecture ne se situe que sur deux niveaux.

Le module SPI communique avec le décodeur et les générateurs de pulse pour charger les différentes données. Le décodeur ne fait que placer l'adresse et gérer le registre de statut.

Architecture du fichier TOP

L'ensemble du design fonctionne sur la même horloge nommée clk et sur un reset asynchrone nommé raza. Ces deux signaux sont câblés sur les entrées suivantes :

Désignation PIN du CPLD Buffer dédié Option Désignation sur la carte
clk P38 BUFG = CLK PCLK (Oscillateur 8 MHz)
raza P143 BUFG = SR SCHMITT_TRIGGER BTN0 (Bouton 0)

Extrait du fichier PulseGenerator_Top.ucf sur le SVN

NET "clk"  LOC = "P38" | BUFG = CLK ;                        
NET "raza"  LOC = "P143" | SCHMITT_TRIGGER  | BUFG = SR ;     # BTN0

Toutes les entrées sont configurées avec un trigger de Schmitt, exceptée l'horloge.

Le fichier de contraintes contient une ligne CONFIG PROHIBIT qui contient l'ensemble des pins qui, soit ne sont pas câblées, soit sont câblées sur des composants de la carte, ceci afin d’empêcher les conflits lors de l'implémentation du design.

On peut changer deux generic dans le fichier TOP qui ont pour effet de configurer les autres modules et changer dynamiquement la taille de certains vecteurs. Leur description est donnée plus bas dans la description des modules.

Les modules dialoguent entre eux sur le principe du protocole AXI4-Stream. Quelques signaux supplémentaires sont toutefois parfois nécessaires pour plus de facilité.

Cette version ne permet pas de relire le contenu des registres.

Liaison SPI

En lien avec :

Exemples VHDL :

Fichiers concernés :

  • SPI_Slave.vhd
  • SPI_Slave_TB.vhd : pour la simulation
  • SPI_Slave_TB.wcfg : pour la configuration des signaux pour la simulation

Les signaux SPI sont câblés de la façon suivante :

Désignation PIN du CPLD Option Désignation sur la carte
SS P10 SCHMITT_TRIGGER J1-IO1
SCLK P3 SCHMITT_TRIGGER J1-IO4
MOSI P7 SCHMITT_TRIGGER J1-IO2
MISO P5 J1-IO3

Extrait du fichier PulseGenerator_Top.ucf sur le SVN

NET "SS"  LOC = "P10" | SCHMITT_TRIGGER ;                    # J1-IO1
NET "SCLK"  LOC = "P3" | SCHMITT_TRIGGER ;                   # J1-IO4
NET "MOSI"  LOC = "P7" | SCHMITT_TRIGGER ;                   # J1-IO2
NET "MISO"  LOC = "P5"  ;                                    # J1-IO3

L'entité du module SPI se compose de trois bus. Le premier est le bus physique SPI qui est câblé sur les pins du composant. Les deux suivants sont les bus AXI4 qui permettent de transmettre les octets reçus ou de charger le registre pour l'envoi de données au maître.

entity SPI_Slave is
   port( clk : in std_logic;
         raza : in std_logic;
         
         -- SPI signals
         SCLK : in std_logic;    -- SPI clock
         MOSI : in std_logic;    -- Master Out Slave In
         MISO : out std_logic;   -- Master In Slave Out
         SS : in std_logic;      -- Slave Select
         
         -- AXI4 : Received data
         TVALID_out : out std_logic;
         TDATA_out : out std_logic_vector(7 downto 0);
         
         -- AXI4 : Data To send
         TVALID_in : in std_logic;
         TREADY_in : out std_logic;
         TDATA_in : in std_logic_vector(7 downto 0) );
end SPI_Slave;

Il n'est possible de charger le registre d'envoi que si le signal SS n'est pas actif (état haut), c'est à dire que le signal TREADY_in n'est actif que lorsque SS est désactivé. En revanche, les données reçues sont transmises sans soucis de savoir si le module qui va le recevoir est prêt à les recevoir, cela dans le but d'empêcher un blocage de la communication. C'est pourquoi le signal TVALID_out n'est actif que pendant un cycle d'horloge. La donnée doit donc être lue pendant ce cycle. Dans le cadre du projet, on sait que les modules concernés sont le décodeur et les générateurs de pulse, pour qui la prise de la donnée est instantanée, donc le risque de perte de données est exclu.

Le SPI est codé pour fonctionner en mode 0<ref>PulseGenerator : Liaison_SPI#Présentation du bus SPI</ref>. Le signal MOSI est donc latché sur un front montant de SCLK et le signal MISO est positionné sur le front descendant.

Le module ne peut pas fonctionner pour l'instant avec d'autres esclaves sur le même bus car il ne gère pas le trois états.

Le module compte le nombre de bits reçus et positionne le flag TVALID_out à chaque fois que 8 bits ont été reçus. Le signal SS doit être maintenu à l'état bas pendant toute la communication. Si le signal repasse à l'état haut avant la réception de tous les bits, le compteur est réinitialisé et la donnée n'est pas transmise au reste du design.

Simulation

La simulation teste l'envoi et la réception de deux octets. On positionne une donnée dans le registre à destination du maître avant le début de la communication. Une tentative pour charger ce même registre pendant l'envoi n'a aucune répercussion. Cela permet de voir par la même occasion que la donnée qui est alors envoyée au maître lors du second envoi est la dernière donnée reçue. On se servira de ce principe pour retransmettre automatiquement le dernier octet reçu sans autre intervention du décodeur.

Simulation du module SPI

Décodeur

Fichiers concernés :

  • decodeur.vhd
  • decodeur_TB.vhd : pour la simulation
  • decodeur_TB.wcfg : pour la configuration des signaux pour la simulation

Le décodeur traduit l'adresse envoyée par le maître en un signal One-Hot qui active le registre destinataire de la donnée. Il possède une machine d'état qui l'autorise à enregistrer le premier octet reçu (qui normalement, sauf erreur, est l'adresse du registre dans lequel enregistrer la donnée reçue) et à le convertir. C'est le signal TREADY_out qui déclenche la machine d'état car c'est ce signal qui indique qu'une transmission est en cours. Lors des quatre étapes suivantes de la machine d'état, l'adresse One-Hot est positionnée en sortie. Sinon le reste du temps les bits du vecteur sont maintenus à zéro pour ne pas involontairement charger une donnée en cas de mauvais adressage ou lors de l'enregistrement de l'adresse.

Le vecteur One-Hot d'adressage est déterminé dynamiquement en fonction du nombre d'instances de générateurs de pulse. Les registres sont adressés dans l'ordre suivant (exemple avec deux instances) :

Adresse Valeur One-Hot Registre adressé
0 000001 Registre Trigger de l'instance 1
1 000010 Registre Delay de l'instance 1
2 000100 Registre Width de l'instance 1
3 001000 Registre Trigger de l'instance 2
4 010000 Registre Delay de l'instance 2
5 100000 Registre Width de l'instance 2

Si l'adresse est supérieure à la plage définie, le vecteur One-Hot est mis entièrement à zéro, ce qui permet de n'adresser aucun registre. Le bit d'erreur SOF est alors mis à '1' (cf ci-dessous).

Le décodeur gère aussi le registre STATUS qui contient les bits d'erreurs. A l'heure actuelle, seuls trois bits sont utilisés :

N° Bits Désignation Rôle
0 POR Power On reset : ce bit est actif après le déamrrage et est effacé à la première lecture du registre STATUS
1 SOF Start Of Frame : ce bit est activé lorsque l'adresse ne correspond à aucune adresse du plan mémoire et est effacé automatiquement après lecture
2 EOF End Of Frame : ce bit est activé lorsque le dernier octet reçu est différent de 0xFF et est effacé automatiquement après lecture

Lors de la lecture, les autres bits sont à zéro.

Générateurs de pulse

Fichiers concernés :

  • pulse_generator.vhd
  • pulse_generator_TB.vhd : pour la simulation
  • pulse_generator_TB.wcfg : pour la configuration des signaux pour la simulation

On instancie autant de générateurs de pulse qu'indiqué par le generic SLAVE_NUMBER dans le fichier TOP. Chacun a donc le même comportement mais les registres internes peuvent être configurés indépendamment.

Le module possède un port AXI4 pour charger les registre internes. Ces registres sont sélectionnés par les entrée reg_select_XXX gérés par le décodeur.

Il possède deux entrées pour le trigger et le arming; et deux sorties pour delay et pulse.

entity Pulse_Generator is
   generic( REGISTER_SIZE : natural range 16 to 256 := 16);
   port( clk : in std_logic;
         raza : in std_logic;
         
         -- AXI4 : Data to write
         TVALID_in : in std_logic;
         TDATA_in : in std_logic_vector(7 downto 0);
         
         -- Vecteur de sélection du registre dans lequel enregistrer
         -- la donnée entrante
         reg_select_trigger : in std_logic;
         reg_select_delay : in std_logic;
         reg_select_width : in std_logic;
         
         -- Signaux externes
         trigger : in std_logic;    -- Gachette
         arming : in std_logic;     -- Armement
         delay : out std_logic;     -- Pulse indiquant le délai d'attente
         pulse : out std_logic );   -- Pulse retardé de la valeur delay de largeur paramètrable
end Pulse_Generator;

Les registres internes sont au nombre de trois. La taille de deux d'entre eux, les registres delay et width, est configurée par le generic REGISTER_SIZE dans le fichier TOP. Le troisième registre est d'une largeur de 4 bits et permet de configurer le comportement des entrées.

Désignation Rôle
0 trigger_pol Permet de choisir entre un front montant (1) et un front descendant (0) pour le déclenchement sur l'entrée trigger
1 trigger_en Active (1) ou désactive (0) le trigger, ce qui a pour effet d'activer ou non la voie de pulse concernée
2 arming_pol Permet de choisir entre un état haut (1) et un état bas (0) pour le déclenchement sur l'entrée arming
3 arming_en Active (1) ou désactive (0) l'utilisation du signal arming

On peut et on doit configurer les entrées / sorties physique pour ces modules. Les E/S trigger, arming, delay et pulse sont concaténées au niveau TOP dans un même vecteur de largeur dépendant du nombre de module instanciés. Par exemple, avec trois modules, le vecteur trigger se nommera "trigger(2 downto 0)" avec la relation suivante :

  • trigger(0) <= trigger de de la voie 1
  • trigger(1) <= trigger de de la voie 2
  • trigger(2) <= trigger de de la voie 3

Par défaut, la première voie est câblée sur les boutons, switch et LEDs tels que suit :

Désignation N° PIN Option Désignation sur la carte Description
trigger P94 SCHMITT_TRIGGER BTN1 (Bouton 1) Entrée gachette
arming P124 SCHMITT_TRIGGER SW1 (Switch 1) Entrée armement
delay P68 LD1 (LED 1) Sortie delay d'attente avant pulse
pulse P69 LD0 (LED 0) Sortie pulse retardé

Extrait du fichier PulseGenerator_Top.ucf sur le SVN

NET "trigger<0>"  LOC = "P94" |SCHMITT_TRIGGER ;            # BTN1
NET "arming<0>"  LOC = "P124" |SCHMITT_TRIGGER ;            # SW1
NET "delay<0>"  LOC = "P68"  ;                              # LD1
NET "pulse<0>"  LOC = "P69"  ;                              # LD0

Simulation

Lors de la simulation, on teste l'ensemble des cas de figure en modifiant le registre trigger. On change donc successivement les polarités de déclenchement sur les signaux trigger et arming ainsi que leur activation ou non.

Les registres delay et width sont configurés au début de la simulation et ne sont plus modifiés par la suite.

Simulation d'un générateur de pulse

Communication avec le PIC

Pour plus de détails sur la communication au niveau du PIC32, vous devez vous rendre à l'adresse suivante.

Le datagrammme de la communication choisi pour cette version est le suivant :

  • Trame pour l'écriture d'un registre interne de 32 bits (trame contenant 6 paquets de 8 bits) :
    n° de l'octet: ......:: 1 ::........:: 2 ::........:: 3 ::........:: 4 ::........:: 5 ::........:: 6 ::.......
    ..............__............................................................................................__
    signal SS  : ...\__________________________________________________________________________________________/
    ..............____.... SOF ....__.. DATA 1 ...__.. DATA 2 ...__.. DATA 3 ...__.. DATA 4 ...__.... EOF ....____
    signal MOSI: .....\__ADDRESS__/..\_MSB VALUE_/..\___VALUE___/..\___VALUE___/..\_LSB VALUE_/..\____0xFF___/
    ..............____.............__.............__.............__.............__.............__.............____
    signal MISO: .....\__STATUS___/..\__ADDRESS__/..\_MSB VALUE_/..\___VALUE___/..\___VALUE___/..\_LSB VALUE_/

Si la taille du registre est inférieur à 32 bits, les bits de poids forts sont ignorés. Par exemple, si on souhaite écrire la valeur 0x123456 dans un registre, on envoi la trame suivante :

  • Trame pour l'écriture d'un registre interne de 24 bits :
    n° de l'octet: ......:: 1 ::........:: 2 ::........:: 3 ::........:: 4 ::........:: 5 ::........:: 6 ::.......
    ..............__............................................................................................__
    signal SS  : ...\__________________________________________________________________________________________/
    ..............____.... SOF ....__.. DATA 1 ...__.. DATA 2 ...__.. DATA 3 ...__.. DATA 4 ...__.... EOF ....____
    signal MOSI: .....\__ADDRESS__/..\_XXXXXXXXX_/..\____0x12___/..\____0x34___/..\____0x56___/..\____0xFF___/
    ..............____.............__.............__.............__.............__.............__.............____
    signal MISO: .....\__STATUS___/..\__ADDRESS__/..\_XXXXXXXXX_/..\____0x12___/..\____0x34___/..\____0x56___/

C'est le cas en particulier pour le registre trigger. Seuls les 4 derniers bits de donnée de la communication sont pris en compte, les autres sont ignorés.

Si la taille du registre de destination est supérieur à 32 bits, il suffit d'envoyer le contenu de la donnée en plusieurs fois, en commençant par les bits de poids forts. Les données envoyées étant toujours multiple de 32 bits, il faut remplir les bits inutilisés avec des valeurs qui de toute façon ne seront pas prises en considération.

L'exemple suivant montre l'écriture de la donnée 0x123456789A dans un registre de 40 bits :

  • Trames pour l'écriture d'un registre interne de 24 bits :
  • Trame 1
n° de l'octet: ......:: 1 ::........:: 2 ::........:: 3 ::........:: 4 ::........:: 5 ::........:: 6 ::.......
..............__............................................................................................__
signal SS  : ...\__________________________________________________________________________________________/
..............____.... SOF ....__.. DATA 1 ...__.. DATA 2 ...__.. DATA 3 ...__.. DATA 4 ...__.... EOF ....____
signal MOSI: .....\__ADDRESS__/..\_XXXXXXXXX_/..\_XXXXXXXXX_/..\_XXXXXXXXX_/..\____0x12___/..\____0xFF___/
..............____.............__.............__.............__.............__.............__.............____
signal MISO: .....\__STATUS___/..\__ADDRESS__/..\_XXXXXXXXX_/..\_XXXXXXXXX_/..\_XXXXXXXXX_/..\____0x12___/
  • Trame 2
n° de l'octet: ......:: 1 ::........:: 2 ::........:: 3 ::........:: 4 ::........:: 5 ::........:: 6 ::.......
..............__............................................................................................__
signal SS  : ...\__________________________________________________________________________________________/
..............____.... SOF ....__.. DATA 1 ...__.. DATA 2 ...__.. DATA 3 ...__.. DATA 4 ...__.... EOF ....____
signal MOSI: .....\__ADDRESS__/..\____0x34___/..\____0x56___/..\____0x78___/..\____0x9A___/..\____0xFF___/
..............____.............__.............__.............__.............__.............__.............____
signal MISO: .....\__STATUS___/..\__ADDRESS__/..\____0x34___/..\____0x56___/..\____0x78___/..\____0x9A___/

Notes

<references/>