Activité : intégration d'un contrôleur de bus SPI

Le bus SPI

Un bus SPI (Serial Peripheral Interface) est un bus série synchrone souvent utilisé pour échanger des données entre un microcontrôleur et un ou plusieurs périphériques. Dans le cadre de ce projet, il permettra à notre application de dialoguer avec des modules accéléromètre, joystick, ou écran OLED par exemple.

Certains périphériques, comme l’écran OLED que nous proposons, possèdent des broches supplémentaires qui ne font pas partie du bus SPI.

Maître et esclave SPI

Historiquement, les électroniciens utilisent les termes « maître » et « esclave » (ou « master » et « slave ») pour désigner les rôles joués par les composants d’un système lorsqu’ils communiquent ensemble. Il s’agit d’une analogie qui vise à faciliter la compréhension : dans un ordinateur, on dira que le processeur se comporte comme un maître, et ses périphériques comme des esclaves.

Il ne s’agit pas de faire la promotion de l’esclavage, ni d’en minimiser la gravité.

Cependant, depuis plusieurs années, cette terminologie est remise en question. La communauté des informaticiens et électroniciens remplace progressivement les mots « maître » et « esclave » par des termes plus neutres. À notre connaissance, il n’existe pas encore de consensus sur le choix de ces nouveaux termes. Pour cette raison, cette page continue à utiliser les mots « maître » et « esclave » qui restent très présents dans les documentations techniques des constructeurs et dans les spécifications de protocoles.

Dans une communication SPI, le maître est le composant qui a l’initiative des communications. L’esclave répond aux demandes du maître. Le maître produit le signal d’horloge qui servira à cadencer l’émission et la réception des bits de données. Les données sont échangées sur deux lignes souvent nommées MISO (Master In Slave Out) et MOSI (Master Out Slave In). On trouve également les dénominations SDI (Serial Data In) et SDO (Serial Data Out).

Maître et esclave SPI

La figure ci-dessus illustre le fonctionnement typique de deux composants SPI. Le maître et l’esclave possèdent chacun un registre à décalage qui sert à la fois à l’émission et à la réception. Ces deux registres sont reliés de manière à former un anneau. Si on se place du point de vue du maître, la transmission d’une valeur se passe de la manière suivante. À chaque période de l’horloge série :

Dans une transmission SPI, l’envoi et la réception des données se font en parallèle. Dans l’exemple représenté ci-dessus, au bout de 8 périodes d’horloge, le maître et l’esclave ont simplement échangé les contenus de leur registres respectifs.

Le protocole SPI

La synchronisation des données sur l’horloge série est définie par deux paramètres.

La figure suivante représente une trame SPI pour un échange de 8 bits :

Le protocole SPI

Ces deux paramètres autorisent quatre variantes du protocole SPI. Pour que la communication ait lieu sans erreur, le maître et l’esclave doivent avoir les mêmes réglages de polarité et de phase.

Compléter le projet Vivado

Si ce n’est pas déjà fait, ouvrez votre projet Computer dans Vivado :

cd $HOME/CoCiNum
./scripts/vivado vivado/Computer/Computer.xpr

Dans le panneau, Flow Navigator, exécutez l’action Add Sources, choisissez Add or create design sources et pressez le bouton Next.

Ajoutez les fichiers source VHDL suivants à votre projet. Tous ces fichiers sont situés dans des sous-dossiers de CoCiNum/src/vhdl.

Sous-dossier Fichier Rôle
SPI SPIMaster-precompiled.vhd Un périphérique contrôleur de bus SPI.

Nous ne fournissons pas le code source du contrôleur SPI.

Le fichier SPIMaster-precompiled.vhd contient du code VHDL qui a fait l’objet d’une première étape de synthèse logique. Il n’est pas destiné à être lu par un être humain.

Choisissez sur quel connecteur d’extension Pmod de la carte Basys3 vous allez brancher votre périphérique SPI. Sur la sérigraphie de la carte, ils portent les noms JA (en haut à gauche), JB (en haut à droite), JC (en bas à droite), et JXADC (en bas à gauche).

Dans le panneau, Flow Navigator, exécutez à nouveau l’action Add Sources, choisissez Add or create constraints et pressez le bouton Next.

Ajoutez au projet Vivado le fichier de contraintes correspondant à votre choix :

Sous-dossier Fichier Rôle
Basys3 Basys3_PmodA.xdc Fichier de contraintes pour Vivado, brochage du connecteur JA.
Basys3 Basys3_PmodB.xdc Fichier de contraintes pour Vivado, brochage du connecteur JB.
Basys3 Basys3_PmodC.xdc Fichier de contraintes pour Vivado, brochage du connecteur JC.
Basys3 Basys3_PmodXADC.xdc Fichier de contraintes pour Vivado, brochage du connecteur JD.

L’entité SPIMaster

L’entité SPIMaster est conçue comme un périphérique pour le processeur Virgule avec les ports suivants :

Port Direction Type Rôle
clk_i Entrée Logique Le signal d’horloge global
reset_i Entrée Logique La commande de réinitialisation
valid_i Entrée Logique Demande de transfert de donnée
ready_o Sortie Logique Indicateur de fin d’une lecture ou d’une écriture
write_i Entrée Logique La commande d’écriture
address_i Entrée Vecteur de 2 bits Le bus d’adresses
wdata_i Entrée Vecteur de 8 bits Le bus de données en écriture
rdata_o Sortie Vecteur de 8 bits Le bus de données en lecture
evt_o Sortie Logique Indique la fin d’un échange de données
miso_i Entrée Logique Les données série en provenance de l’esclave
mosi_o Sortie Logique Les données série à destination de l’esclave
sclk_o Sortie Logique L’horloge de communication série
cs_n_o Sortie Logique Commande de sélection de l’esclave (Chip Select)

L’entrée address_i, sur deux bits, permet d’accéder à trois registres de 8 bits. À l’adresse "01", seuls trois bits sont effectivement utilisés :

address_i Bits Registre Rôle
"00" 7 à 0 data_reg La donnée envoyée ou reçue.
"01" 0 cs_reg La commande de sélection de l’esclave
"01" 1 phase_reg La phase de l’horloge SPI
"01" 2 polarity_reg La polarité de l’horloge SPI
"10" 7 à 0 timer_max_reg La limite du compteur pour régler la vitesse de communication.

Pour échanger une séquence d’octets sur le bus SPI, il faut :

  1. Régler polarity_reg et phase_reg selon les caractéristiques de l’esclave. Mettre cs_reg à '1'.
  2. Attendre pendant une durée qui dépend de l’esclave.
  3. Écrire l’octet à envoyer dans data_reg.
  4. Attendre que evt_o passe à '1'.
  5. Lire l’octet reçu dans data_reg.
  6. Répéter les étapes 2 à 5 tant qu’il reste des octets à échanger.
  7. Attendre pendant une durée qui dépend de l’esclave.
  8. Remettre cs_reg à '0'.
  9. Attendre pendant une durée qui dépend de l’esclave avant de commencer une nouvelle séquence.

On utilisera un timer pour mesurer le temps dans les étapes 2, 7 et 9.

La sortie evt_o sera typiquement reliée au contrôleur d’interruptions.

Modification du système

Paquetage Computer_pkg

Dans le fichier Computer_pkg.vhd, ajoutez des constantes pour définir les caractéristiques des nouveaux périphériques du système :

Constante Type Valeur Rôle
SPI_TIMER_ADDRESS Octet 84hex Les bits 31 à 24 de l’adresse pour accéder au timer de gestion des communications SPI.
SPI_MASTER_ADDRESS Octet 85hex Les bits 31 à 24 de l’adresse pour accéder au contrôleur SPI.
INTC_EVENTS_SPI_TIMER Entier 3 Pour le contrôleur d’interruptions, le numéro de l’événement indiquant la fin d’un cycle de comptage du timer SPI.
INTC_EVENTS_SPI_MASTER Entier 4 Pour le contrôleur d’interruptions, le numéro de l’événement indiquant la fin d’une communication SPI.

Les informations contenues dans la colonne Valeur sont des exemples. Vous pouvez les modifier à condition que chaque périphérique de votre architecture ait une adresse et un numéro d’événement différents des autres périphériques.

Entité Computer

Dans le fichier Computer.vhd, complétez l’entité Computer en déclarant les ports du connecteur d’extension que vous avez choisi. Le tableau ci-dessous donne la liste des ports disponibles et leur rôle. Ils sont tous de type std_logic.

Rôle JA JB JC JXADC Mode
Chip Select pmod_a1 pmod_b1 pmod_c1 pmod_xadc1 out
Master Out Slave In pmod_a2 pmod_b2 pmod_c2 pmod_xadc2 out
Master In Slave Out pmod_a3 pmod_b3 pmod_c3 pmod_xadc3 in
Serial Clock pmod_a4 pmod_b4 pmod_c4 pmod_xadc4 out

Architecture Structural

Dans le fichier Computer.vhd, complétez l’architecture pour intégrer un contrôleur SPI et un second timer dans le système. Utilisez les constantes que vous avez ajoutées au paquetage Computer_pkg.

Par précaution, le port d’entrée correspondant à la ligne Master In Slave Out devra être resynchronisé. Ajoutez une entrée et une sortie à l’instance sync_inst de la manière suivante :

sync_inst : entity work.InputSynchronizer
    generic map(
        WIDTH => 19
    )
    port map(
        ...
        data_i(18) => ...,          -- Votre port d'entrée Master In Slave Out
        data_o(18) => sync_spi_miso -- Pensez à déclarer ce signal
    );

Pensez à déclarer le signal sync_spi_miso et à le relier à l’entrée miso_i de votre contrôleur SPI.

Inspirez-vous des instances déjà présentes dans l’architecture.

Représentez sous la forme d’un schéma la nouvelle structure du système.

Si votre périphérique possède des broches supplémentaires, ajoutez la gestion de ces broches avant de passer à la suite. Voir en particulier la page Interfaçage d’un écran OLED.

Générer le bitstream et configurer le FPGA

Dans Vivado, générez le fichier binaire à charger dans le FPGA : Flow NavigatorProgram and DebugGenerate Bitstream.

Si ce n’est pas déjà fait, reliez le connecteur micro-USB de la carte à un port USB de votre PC et mettez la carte sous tension.

Connectez Vivado à votre carte Basys3 : Flow NavigatorProgram and DebugOpen Hardware ManagerOpen TargetAuto-connect.

Configurez le FPGA : Flow NavigatorProgram and DebugOpen Hardware ManagerProgram Device.

Si vous n’avez pas d’autre périphérique à ajouter, vous pouvez passer à la section Développement logiciel embarqué.