Universal Asynchronous Receiver Transmitter Liaison série asynchrone Nous allons nous intéresser à un composant UART permettant de transmettre et recevoir des données à travers un port série standard RS232. La communication série nécessite 2 signaux : TxD et RxD. Ce dernier se réfère aux données reçues, et le premier aux données transmises. Le port série standard est composé de 9 broches : on peut le trouver notamment sur des PC, avec généralement un connecteur mâle disposant de 2 broches pour le RxD et 3 pour le TxD 1. Lorsque les connecteurs sont reliés, le TxD de l un est connecté au RxD de l autre, et vice versa, afin que les données transmises par l un soient reçues par l autre. Les données sont transférées à une vitesse spécifique que l on appelle baud rate, et qui s exprime en bits par seconde. On trouve classiquement des baud rates de 4800, 9600, 56k et 115,5k. L émetteur et le récepteur doivent s accorder sur le baud rate, le nombre de bits de données, la parité, le nombre de bits de stop. Dans la plupart des implémentations, on transmet de 5 à 8 bits de données, avec parité paire, impaire ou sans parité, et un ou deux bits de stop. Au cours de cet examen, afin de simplifier, nous utiliserons 8 bits de données, pas de parité et 1 bit de stop. La transmission série est synchronisée par le biais d un bit de start qui précède la transmission et un bit de stop qui indique que le dernier bit a été transmis, ce qui termine l envoie de l octet de donnée. Les codes ASCII sont souvent utilisés pour communiquer entre un PC et un appareil. La figure 1 montre la séquence de bit pour la transmission du code ASCII de «T», 0x54. Figure 1 : Code ASCII 0x54 = 0101 0100 («T») transmis sans parité Nous allons nous focaliser ici sur la conception du composant uart_tx pour transmettre les données en série suivant le protocole défini ci-dessus. La figure 2 illustre l interface de ce composant. Figure 2. Interface du composant UART_TX Ce composant dispose d une entrée clk pour synchroniser la communication et rst pour l initialiser. Un octet de données est positionné sur l entrée Tx_data. Quand le port ready est à l état haut, cet octet est transmis sur TxD en commençant par le LSB. La transmission commence par l émission du bit de start (passage de l état de 1 à 0 ). La durée de 1 bit dépend du baud rate comme nous l avons vu auparavant. Après ce bit de start commence la transmission de l octet de data, en commençant par le bit 0 (LSB), bit 1, bit 2, etc., jusqu au bit 7 (MSB) et enfin le bit de stop qui marque la fin de la trame série. Le diagramme d états décrivant le fonctionnement du bloc UART_TX est représenté sur la figure 3. 1 Sur la Nexys 3, il s agit d un USB UART, qui tend à remplacer les connexions traditionnelles. Il peut néanmoins s utiliser exactement de la même manière.
Figure 3. Diagramme d états simplifié pour la transmission des données en série 1) D après ce que nous avons vu en cours, comment se nomme ce diagramme d états? Il s agit d une Machine à Etats Finis. 2) Dans cette représentation, les sorties ne dépendent que de l état courant : de quel «type» de machine s agit-il? Machine de MOORE. (cf. cours MEF) 3) Précisez à quoi se réfèrent les éléments en vert, en rouge et en bleu, les cercles et les arcs. VERT -> NOM DES ETATS, ROUGE -> ENTREES, BLEU -> SORTIES, CERCLES -> ETATS, ARCS -> TRANSITIONS 4) Le composant UART_TX est cadencé par clk, une horloge à 100MHz. Déterminer le nombre de coups d horloges nécessaires à cette fréquence pour obtenir un baud rate de 9600 bauds. 9600 bauds correspond à 9600 bits par seconde, à 100 MHz il faut 10417 (=100.10 6 /9600) coups d horloge pour obtenir ce baud rate. 5) réaliser la conception du bloc UART_TX en expliquant votre démarche et le choix des composants. Indications supplémentaires: Le générateur d horloge interne :
Utiliser un générateur d horloge interne au bloc UART_TX, qui permet de générer une horloge clk_tx correspondant à une impulsion de 10ns toutes les secondes use IEEE.STD_LOGIC_UNSIGNED.ALL; entity UART_TX_CLK_GEN is Port ( mclk : in STD_LOGIC; -- 100 MHz clk_tx : out STD_LOGIC); end UART_TX_CLK_GEN; architecture Behavioral of UART_TX_CLK_GEN is signal count: std_logic_vector(27 downto 0); constant N: std_logic_vector(27 downto 0):=x"5F5E100"; -- pour un clk_tx de 1s --constant N: std_logic_vector(27 downto 0):=x"0000010"; -- pour la simul, on prend N petit process(mclk,rst) if rst='1' then count<=(others=>'0'); clk_tx<='0'; elsif mclk'event and mclk='1' then if count=n then clk_tx<='1'; count<=(others=>'0'); else clk_tx<='0'; count<= count+1; end process; Le registre de données à décalage: Utiliser un registre à décalage interne au bloc UART_TX permettant de charger les données tx_data dans l état IDLE (commande load), et sortir en série (commande shift) ces mêmes données telles que cela est décrit dans la figure 1. entity UART_TX_BUFF is Port ( clk_tx : in STD_LOGIC;
tx_data_par : in STD_LOGIC_vector(7 downto 0); shift : in STD_LOGIC; load : in STD_LOGIC; tx_data_ser : out STD_LOGIC); end UART_TX_BUFF; architecture Behavioral of UART_TX_BUFF is signal tx_data_buf: std_logic_vector(7 downto 0); process(rst,clk_tx) if rst='1' then tx_data_ser<='0'; elsif clk_tx'event and clk_tx='1' then if load='1' then tx_data_buf<=tx_data_par; if shift='1' then tx_data_ser<=tx_data_buf(0); tx_data_buf(6 downto 0)<= tx_data_buf(7 downto 1); end process; Le compteur de bits : Si ce composant n est pas explicitement mentionné dans l énoncé, il apparaît nécessaire de compter le nombre de bits à transmettre tel qu on l observe dans la figure 3 (état SEND). S il est tentant (et possible) de placer le processus de comptage directement dans la MEF, ceci est cependant déconseillé. Il complexifie inutilement la description de la MEF, et il rend sa vérification plus hasardeuse. Pour cette raison, on préfèrera toujours (du moins avant d avoir acquis suffisamment d expérience) une approche «Divide and conquer». On propose donc de passer par un composant très simple réalisant la fonction de comptage tel que décrit ci-dessous. use IEEE.STD_LOGIC_UNSIGNED.ALL; entity bit_count is Port ( clk : in STD_LOGIC; ena : in STD_LOGIC; count : out STD_LOGIC_VECTOR (2 downto 0)); end bit_count; architecture Behavioral of bit_count is signal count_int: STD_LOGIC_VECTOR (2 downto 0); process(clk, rst) if rst='1' then count_int<="000"; elsif clk'event and clk='1' then if ena='1' then count_int<= count_int+1;
end process; count<=count_int; Le multiplexeur de sortie: Une fois encore, ce composant n est pas directement explicité dans l énoncé. Il n est pas indispensable, mais il permet de dissocier la gestion des commandes et la transmission des données. entity UART_TX_OUTMUX is Port ( tx_data_ser : in STD_LOGIC; sel : in STD_LOGIC_VECTOR (1 downto 0); TxD : out STD_LOGIC); end UART_TX_OUTMUX; architecture Behavioral of UART_TX_OUTMUX is mux: process(tx_data_ser, sel) case sel is when "00" => TxD <='1'; -- IDLE / STOP when "01" => TxD <='0'; -- START when "10" => TxD <= tx_data_ser; -- SEND when others => TxD <= '1'; end case; end process; Le contrôleur de l UART : Le contrôleur va être implémenté en suivant le modèle de la MEF décrit dans la figure 3. Si l on récapitule, il y a 3 composants qui doivent être pilotés par cette MEF : le buffer de données, le compteur et le multiplexeur de sortie. La liste des commandes à générer est donc la suivante : - shift et load (registre de données) - ena (compteur) - TxD_mux (multiplexeur de sortie) On rajoute également un indicateur visuel (led) pour observer l état courant sur la carte FPGA. Ceci fixe donc les sorties du contrôleur. Pour les entrées, il y a l horloge (clk_tx) et le reset (rst), mais aussi le signal count et le signal ready (cf. figure 2). Nous pouvons donc à partir de là décrire l interface de notre contrôleur d UART :
entity UART_TX_CTRL is Port ( ready : in STD_LOGIC; clk_tx : in STD_LOGIC; count : in STD_LOGIC_VECTOR (2 downto 0); shift : out STD_LOGIC; load : out STD_LOGIC; ena : out STD_LOGIC; TxD_mux : out STD_LOGIC_VECTOR(1 DOWNTO 0); led : out STD_LOGIC_VECTOR (3 downto 0)); end UART_TX_CTRL; Pour l architecture, nous allons suivre la méthode vue en cours basée sur les 2 process. Mais avant de rentrer dans le codage, on prend soin de compléter la description de la MEF : Ce qui donne pour le code de l architecture : architecture Behavioral of UART_TX_CTRL is TYPE etat_4 IS (IDLE, START, SEND, STOP); SIGNAL etat, nextetat :etat_4 ; --2 signaux :etats courant et suivant seq: PROCESS( rst, clk_tx) -- "definir_etat" :label optionnel BEGIN If rst = '1' THEN etat <= IDLE; ELSIF rising_edge(clk_tx) THEN etat <= nextetat; END IF; END PROCESS; combi: process (etat, ready, count) BEGIN CASE etat IS WHEN IDLE => TxD_mux <="00"; --Sortie IDLE -> '1'
shift <= '0'; --Décalage Y/N (1=Y) load<='1'; ena <='0'; --Comptage Y/N (1=Y) led <="0001"; --Affichage LED ETAT IDLE if ready='1' then nextetat <= START; --Etat suivant si demande d'envoi else nextetat <= IDLE; --Sinon IDLE WHEN START => TxD_mux <="01"; --Sortie START -> '0' load<='0'; shift <= '1'; ena <='0'; led <="0010"; nextetat <= SEND; WHEN SEND => TxD_mux <="10"; --La sortie vaut celle du registre à décalage load<='0'; shift <= '1'; ena <='1'; led <="0100"; if count = "111" then nextetat <= STOP; --Count=8 => 8 bits envoyés else nextetat <= SEND; --Sinon on continue d'envoyer WHEN STOP => TxD_mux <="00"; --Sortie IDLE -> '1' load<='0'; shift <= '0'; ena <='0'; led <="1000"; nextetat <= IDLE; END CASE; END process; Schéma de l architecture globale : Il faut maintenant créer le composant UART_TX_TOP qui va nous permettre d instancier l ensemble des blocs précédents. L interface est décrite dans le bout de code suivant : entity UART_TX_TOP is Port ( mclk : in STD_LOGIC; tx_data : in STD_LOGIC_VECTOR (7 downto 0); ready : in STD_LOGIC; TxD : out STD_LOGIC; led : out STD_LOGIC_VECTOR (3 downto 0)); end UART_TX_TOP; Pour connecter ces composants, il faut évidemment passer par des signaux internes non présents dans la liste des ports. L architecture finale est décrite dans la copie d écran d ISE suivante.
La simulation (en choisissant une valeur de N adéquate dans le générateur de CLK_TX) doit permettre de valider l enchaînement des états ainsi que la transmission des données en série. Implémentation sur carte NEXYS 3 : Il ne reste plus alors qu à adapter le TOP pour bénéficier des entrées / sorties de la carte et valider le fonctionnement de la transmission série. Pour cela, on peut par exemple utiliser l interface suivante, en veillant bien à éditer correctement le fichier UCF : entity UART_TX_TOP_NEXYS3 is Port ( mclk : in STD_LOGIC; btnu : in STD_LOGIC; btns : in STD_LOGIC; Led : out STD_LOGIC_VECTOR (4 downto 0); sw : in STD_LOGIC_VECTOR (7 downto 0)); end UART_TX_TOP_NEXYS3; On utilise dans ce cas les switch pour générer l octet à transmettre, la 5 ème Led est utilisée pour vérifier la transmission série de cet octet, btnu et btns sont utilisés pour le reset et le signal «ready». On peut bien sûr améliorer encore l interface, en utilisant par exemple les afficheurs 7-segments pour afficher les bits transmis.