\subsection{Cahier des charges} Voici les contraintes du bloc d'entrées / sorties : \begin{itemize} \item{Bloc de 8 entrées / sorties} \item{Chaque fil est configurable soit en entrée, soit en sortie} \item{Chaque fil est configurable pour générer une interruption sur front montant et / ou front descendant} \item{Un signal de RAZ} \end{itemize} % en gros c'est comment est vu le module côté userland. \subsection{Vue comportementale} Les blocs d'entrées / sorties ont plusieurs utilités. La première est de pouvoir brancher des capteurs renvoyant une entrée binaire, par exemple la sortie d'un comparateur. La deuxième est de permettre de commander, des sorties comme par exemple l'allumage de LEDs. Finalement la troisième fonction de ce bloc sera de simuler un bus parallèle. En effet, l'année dernière, nous utilisions un bus parallèle pour lequel nous avons développé un certain nombre de cartes. A partir de blocs d'entrées / sorties, on peut donc réutiliser les modules précédements développés. On pourra voir la figure \ref{entity_gpio} page \pageref{entity_gpio} pour avoir une vue globale du module. \begin{figure}[htbp] \caption{Entity du bloc d'entrées / sorties} \begin{center} \scalebox{0.6}{ \includegraphics {./gpio/images/entity.pdf} %\includegraphics[width=\textwidth]{./interrupt/images/entity.pdf} } \end{center} \label{entity_gpio} \end{figure} Du point de vue comportemental son fonctionnement est le suivant. Au début, le PC configure le bloc après avoir envoyé un signal de remise à zéro. Il commence par configurer les différentes pins du bloc soit en entrées, soit en sorties. Ensuite le PC configure quelles pins pourront générer une interruption. Ces interruptions peuvent être déclenchées soit sur front montant, soit sur front descendant. Une fois cela fait, il peut lire et écrire des données sur les entrées et sorties. La configuration du bloc gpio est réalisée en écrivant dans des registres. On remarque que ce module possède cinq signaux "chip select" : cs\_reg\_data, cs\_reg\_direction, cs\_reg\_it\_up\_mask, cs\_reg\_it\_down\_mask et cs\_read\_output. Le signal cs\_read\_output permet de lire les états des 8 broches d'entrées / sorties. Les autres signaux permettent d'accéder aux registres contenant respectivement, le masque des directions, les masques d'interruptions (font montant et descendant) et les données à mettre sur les lignes en sorties. Ces quatres registres ont la particularité de pouvoir être écrit mais aussi lu. En effet, pour la phase de teste il nous semble utile que les registres puissent être relus. Ainsi on pourra s'assurer que la configuration est bien celle désirée. Voici comment se déroule les procédures d'écriture et de lecture dans les registres. La procédure d'écriture est la suivante. Tout d'abord, le module dirigeant le bloc écrit sur le bus de donnés l'octet lui permettant de configurer tel ou tel registre. Simultanément, il positionne le signal $R\bar{W}$ en mode écriture (Write). Finalement, il sélectionne à l'aide d'un des signal CS (Chip select) le registre dans lequel il veut écrire. La procédure de lecture s'effectue en positionnant le signal $R\bar{W}$ en lecture (Read), puis en sélectionnant le chip select du registre auquel on désire accéder. On notera que pour interfacer ce bloc et le bus ISA, il faut un module dont le rôle sera de convertir les signaux du bus ISA (AEN, IOR, IOW) en les signaux $R\bar{W}$ et les signaux de "chip select" (CS). Pour résumer, voici quelques informations utiles : \begin{itemize} \item{La lecture (signal $R\bar{W}$) : active pour un état haut} \item{L'écriture (signal $R\bar{W}$) : active pour un état bas} \item{Les masques d'interruptions : interruption active pour un état haut, inactive pour un état bas} \item{Le masque de directions : en sortie pour un état haut, en entrée pour un état bas} \end{itemize} \subsection{Exemple d'utilisation du module} Voici un exemple pour illustrer les discours précédents. On pourra se reporter au chronogramme figure \ref{gpio_behav_sim1} page \pageref{gpio_behav_sim1} Ce chronogramme représente la simulation comportementale du bloc d'entrées sorties. %% TODO : Se débrouiller pour inclure les DEUX images % Commande sympas : convert -rotate -90 fichier.tiff fichier.pdf %% \begin{figure}[htbp] %% \begin{center} %\includegraphics[height=\textheight,angle=90]{./gpio/sim/gpio_behav_sim_test.pdf} %% \includegraphics[height=0.8\textheight,angle=90]{./gpio/sim/gpio_behav_sim_dim.pdf} %\includegraphics[width=0.7\textwidth,angle=90]{./gpio/sim/gpio_behav_sim1.pdf} %% \end{center} %% \label{gpio_behav_sim1} %% \end{figure} Dans cet exemple, nous allons configurer puis utiliser le bloc d'entrées / sorties. La première action à effectuer est une remise-à-zéro du bloc à l'aide de son signal reset. C'est ce que l'on remarque lorsque le signal rst est à 1. Ce signal doit garder un état haut durant au moins une prériode d'horloge du bus (clk\_b). On remarque que sur les entrées /sorties, on simule le fait d'avoir branché des capteurs sur les cinq lignes de poids fort. On voit donc apparaître sur la sortie les valeurs $00001ZZZ$. Cela signifie que l'on laisse les trois pins de poid faible dans un état de haute impédance. Une fois cela fait, on configure alors le bloc. Pour ce faire, l'ordinateur place tout d'abord sur le bus de données le masque de direction. Dans l'exemple, le masque est $00000111$, ce qui signifie que l'on désire positionner les 3 pins de poid faible en sorties. On peut alors constater que les trois pins du bloc qui sont en sortie, prennent la valeur $0$. Cela vient du fait que lors du reset, on a remis le bloc à zéro, et par défaut, les sorties sont à zéro d'ou les signaux obtenus. On remarquera aussi que la ligne $R\bar{W}$ est en écriture. Ainsi, dès que la ligne de validation du registre de configuration des directions (ligne chip select : cs\_reg\_direction ) est à un état haut et qu'il y a un front montant de l'horloge (clk\_b) du bus, on configure le bloc avec les signaux sur le bus de données. De la même manière, on configure ensuite les masques d'interruptions. On positionne la valeur $11100000$ dans le registre reg\_it\_up et la valeur $00011000$ dans le registre reg\_it\_down. Dans cette configuration, on vient d'autoriser les interruptions sur les pins en entrée du bloc. Les registres ainsi positionnés vont donc autoriser le déclenchement des interruptions des 3 bits de poid fort sur front montant, et des bits 4 et 5 sur front descendant. Ensuite, on transmet au bloc les signaux que l'on désire voir sur les lignes en sortie du bloc. Dans notre cas on transmet $00000001$. Comme seules les trois pins de poid faible sont en sortie, on recopie en sortie $ZZZZZ001$ sans tenir compte des bits de poid fort. Comme les "capteurs simulés" sur les cinq bits de poid fort génèrent les valeurs $00001ZZZ$, on retrouve sur la sortie un mélange de ces signaux, d'où la valeur de $00001001$. Dans la suite, on se propose de relire les valeurs affectées aux différents registres. C'est ce que l'on fait. On vérifie successivement les masques d'interruptions, le registre de direction puis le registre de données. Finalement le PC lit les valeurs situées sur les entrées sorties, ce qui est réalisé grâce au signal cs\_read\_output. On remarquera aussi le signal d'interruption. Celui-ci passe à l'état haut lorsque l'état des "capteurs" change. Ici, on remarquera que c'est un front descendant qui génère cette interruption. Le bloc d'entrées /sorties attend alors que le PC demande à lire les entrées / sorties pour achever l'interruption. \subsection{Architecture physique} Nous allons dans cette partie étudier plus en détails le fonctionnement interne du module d'entrées /sorties. On pourra consulter l'architecture physique sur la figure \ref{archi_gpio} page \pageref{archi_gpio}. \begin{figure}[htbp] \caption{Architecture physique du bloc d'entrées / sorties} \begin{center} %\scalebox{0.7}{\includegraphics {./interrupt/images/archi_phy.pdf}} \includegraphics[width=\textwidth]{./gpio/images/archi_phy.pdf} \end{center} \label{archi_gpio} \end{figure} Comme on peut le constater, ce module est relativement simple. On peut le décomposer en deux parties : la configuration et la lecture / écriture des entrées et sorties. Nous commencerons par nous pencher sur la configuration du bloc. Nous avons vu précédement que pour la configuration d'un bloc d'entrées / sorties, il y avait trois registres de configuration. Un registre contenant un masque déterminant la direction de chaque pin et deux registres contenant des masques d'interruptions. On retrouve bien ces trois registres sur le schéma directement connectés au bus de données. On trouve un troisième registre nommé registre de données. Celui-ci est chargé de mémoriser les valeurs à mettre sur les pins du bloc qui sont configurées en sortie. Pour revenir aux registres de configuration, ils sont alors connectés sur d'autres blocs. Le premier, est le gestionnaire de direction et a pour but de recopier les valeurs du registre de données uniquement sur les pins qui sont configurées en sorties. Les deuxièmes et troisièmes sont les gestionnaires d'interruptions qui sont chargés de générer les interruptions. On voit que pour cela ils s'aident des masques d'interruptions "haut" et "bas". Finalement, pour la lecture des entrées / sorties on connecte directement sur les pins un bloc à trois états (état bas, état haut et haute impédance). Ce bloc dès qu'on le sélectionne à l'aide du signal cs\_read\_output recopie les entrées sorties sur le bus de données. On notera que ce module, de même que les trois registres, sont synchronisés sur l'horloge du bus ISA. \subsection{Décomposition RTL} \subsubsection{Les registres : reg\_rw} Commençons par décrire le fonctionnement du registre synchrone nommé \textit{reg\_rw}. Ce registre est un registre 8 bits pas tout à fait standard. Comme les registres standards, il possède une horloge, un signal de remise-à-zéro, un signal enable, des entrées au nombre de 8 et des sorties au nombre de 8 également. Par contre, celui-ci a un signal supplémentaire : le signal Read / Write ($R\bar{W}$). Son entity est donc la suivante : \begin{itemize} \item{1 signal d'horloge} \item{1 signal de remise-à-zéro} \item{8 signaux d'entrée} \item{8 signaux de sortie} \item{1 signal enable} \item{1 signal read / write} \end{itemize} L'intérêt de ce registre par rapport à un registre "normal" est de pouvoir relire son contenu. Cela est utile dans notre cas puisque durant le développement de la carte, nous aurons besoin de pouvoir nous assurer du bon fonctionnement des différents modules de la carte. L'implémentation de ce registre est particulièrement adaptée à notre carte qui communique par bus. On se rappelera que c'est un registre synchrone. Les opérations seront donc effectuée à la suite d'un front montant. Pour résumer son fonctionnement, voici comment ce registre se comporte : \begin{itemize} \item{Si $write$ ($R\overline{W}$ = '0') et $enable$, alors sauvegarde de l'entrée et recopie de celle-ci en sortie.} \item{Si $read$ ($R\overline{W}$ = '1') et $enable$ alors recopie de la dernière valeure sauvegardée sur l'entrée.} \item{Si $\overline{enable}$, alors recopie la dernière valeur sauvegardée sur la sortie et positionne l'entrée en haute impédance.} \end{itemize} Le code correspondant au registre décrit précédement se trouve en annexe~\ref{sec:reg_rw} page~\pageref{sec:reg_rw}. \subsubsection{Le bloc trois-états : tristate} Pour la lecture des valeurs sur les entrées / sorties, un composant nommé \textit{tristate} est utilisé. L'entity de ce module est la suivante : \begin{itemize} \item{8 signaux d'entrée} \item{8 signaux de sortie} \item{1 signal enable} \end{itemize} Ce composant est en fait un module asynchrone composé de huits composants trois états. Son comportement est donc le suivant : \begin{itemize} \item{Si enable (représenté par cs\_read\_output sur le schéma) est à un état bas, les sorties sont dans un état haute impédance (noté 'Z')} \item{Si enable est à l'état bas on recopie les entrées / sorties sur le bus de données} \end{itemize} Le code correspondant au bloc décrit précédement se trouve en annexe~\ref{sec:tristate} page~\pageref{sec:tristate}. \subsubsection{Le gestionnaire de direction : gpio\_direction} Lors de la conception du cahier des charges du bloc du module GPIO, nous avions bien spécifié le fait que chaque pins devait être configurable soit en entrée, soit en sortie. C'est le rôle de ce bloc. Son entity est la suivante : \begin{itemize} \item{8 signaux d'entrée} \item{8 signaux de constituant un masque} \item{8 signaux de sortie} \end{itemize} Celui ci fonctionne donc de la manière suivante. Pour ce faire, celui-ci possède deux "bus" de 8 entrées chacun. Le premier de ces bus est destiné au masque de direction. Le deuxièmes bus est destiné aux données. Le masque est une série de "flags". Si un bit est dans un état haut, alors, ce bit est considéré être en sortie. Ainsi, pour ce flag à l'état haut, on recopiera sur la sortie le bit du bus de données. Les pins autre n'étant pas définies comme des sorties par le masque sont positionnés dans un état haute-impédance 'Z'. Le code correspondant au bloc décrit précédement se trouve en annexe~\ref{sec:gpio_direction} page~\pageref{sec:gpio_direction}. \subsubsection{La détection d'interruptions : gpio\_it\_detect\_up et down} Le but de ces deux modules est de générer les interruptions. Le premier est chargé de générer une interruptions si il y a un front motant. Le deuxième fait la même chose, mais pour les fronts descendants. Les entity de ces deux blocs sont identiques et sont : \begin{itemize} \item{1 signal d'horloge} \item{1 signal de remise-à-zéro} \item{8 signaux d'entrée} \item{8 signaux de constituant un masque} \item{1 signal de sortie (interrupt detected)} \end{itemize} Chacune des 8 pins devant être configurable pour déclencher une interruption ou non, nous avons recours à des masques. Chaque gestionnaire à son propre masque. De cette manière, on peut définir les masques de telle manière qu'une pin puisse générer une interruption, sur front montant, descendant ou sur les deux. Le code associé au gestionnaire d'interruption sur front montant se trouve en annexe~\ref{sec:gpio_it_detect_up} page~\pageref{sec:gpio_it_detect_up}. Le code associé au gestionnaire d'interruption sur front descendant est donné annexe~\ref{sec:gpio_it_detect_down} page~\pageref{sec:gpio_it_detect_down}. \subsubsection{Le module gpio} Ce bloc est celui dont le comportement aura été décrit précédement dans les sections en rapport avec l'architecture physique. On pourra d'ailleurs se rapporter aux différents schémas précédement vus. Voici l'entity de ce bloc : \begin{itemize} \item{1 signal d'horloge} \item{1 signal d'horloge de bus} \item{1 signal de remise-à-zéro} \item{1 signal read / write} \item{1 signal d'interruption} \item{8 signaux associés au bus de données} \item{4 signaux pour accéder aux différents registres} \item{1 signal pour lire les valeurs sur les entrées / sorties} \end{itemize} Ce bloc est donc l'assemblage des différents modules qui viennent d'être décrits. Le listing correspondant à ce module est donné annexe~\ref{sec:gpio} page~\pageref{sec:gpio}. \subsection{Résultats de synthèse logique} Ci dessous sont présentées un résumé des informations fournies suite à la synthèse logique du module. \lstinputlisting{./gpio/res_gpio.syr} On peut donc constater que les ressources qui seront utilisées par un module gpio, sont daptées au FPGA que nous avons choisi, à savoir, un Spartan2 xc2s200 avec un boitier pq208. En effet, nous avons décidé d'avoir 5 modules gpio sur la carte, ce qui représente 40 pins utilisées et 5\% des ressources en slices. Ces caractéristiques nous conviennent parfaitement. \subsection{Simulation du bloc et test sur carte} Voici les résultats de la simulation RTL de notre bloc d'entrées / sorties. %% TODO : Se débrouiller pour inclure l'images On pourra vérifier que celle-ci est bien conforme aux résultats escomptés en se reportant à la simulation comportementale. Après cette synthèse, nous avons programmé une carte de test pour vérifier qu'il n'y ai pas d'anomalie et nous avons pu constater que tout se déroule comme prévu !