OpenCores
URL https://opencores.org/ocsvn/saturn/saturn/trunk

Subversion Repositories saturn

[/] [saturn/] [trunk/] [IPCommunication/] [com_exec.vhd] - Rev 2

Compare with Previous | Blame | View Log

--============================================================================= 
--  TITRE : COM_EXEC
--  DESCRIPTION : 
--       Exécute les actions codées dans la trame applicative 
--       Gère des registres Internes (zone d'adresse de 0 à 7F)
--       Gère les écritures et lectures des registres externes 
--       (zone d'adresse de 80 à FF)
 
--  FICHIER :        com_exec.vhd 
--=============================================================================
--  CREATION 
--  DATE	      AUTEUR	PROJET	REVISION 
--  10/04/2014	DRA	   SATURN	V1.0 
--=============================================================================
--  HISTORIQUE  DES  MODIFICATIONS :
--  DATE	      AUTEUR	PROJET	REVISION 
--=============================================================================
 
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
 
ENTITY com_exec IS
   GENERIC (
      freq_clksys : INTEGER := 48;                    -- Fréquence de l'horloge système en MHz
      reg_typemio : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"10";  -- Type du MIO
      reg_version : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"10";  -- Version du MIO
      ad_ref      : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"80";  -- Adresse des registre à transmettre sur synchro
      sz_ref      : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"04"   -- Nombre d'octets à transmetrte sur synchro
      );
   PORT (
      -- Ports système
      clk_sys     : IN  STD_LOGIC;                    -- Clock système
      rst_n       : IN  STD_LOGIC;                    -- Reset général système
      tid         : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); -- Adresse affectée au MIO (8F au reset)
      iid         : IN  STD_LOGIC_VECTOR(63 downto 0);-- Adresse IID du composant
      sync_lock   : OUT STD_LOGIC;                    -- Indique qu'on est calé sur une synchro
 
      -- Interfaces vers le bloc métier
      datout_write: OUT STD_LOGIC_VECTOR(7 DOWNTO 0); -- Données à écrire sur l'interface externe
      datout_read : IN STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Données lues sur l'interface externe
      ad_out      : OUT STD_LOGIC_VECTOR(6 DOWNTO 0); -- Adresse d'écriture et de lecture des données externes
      wr_out      : OUT STD_LOGIC;                    -- Signal d'écriture sur l'interface externe
      rd_out      : OUT STD_LOGIC;                    -- Signal de lecture sur l'interface externe
 
      -- Interfaces vers les modules layer2_rx
      activity1   : IN STD_LOGIC;                     -- 1 pulse indique qu'il y'a de l'activité sur le port de com 1
      activity2   : IN STD_LOGIC;                     -- 1 pulse indique qu'il y'a de l'activité sur le port de com 2
 
      -- Interfaces vers le module frame_store1
      datin1      : IN STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Données utiles de la couche applicative (commande, @, data)
      socin1      : IN STD_LOGIC;                     -- Indique que l'octet sur datin1 est le 1er d'une commande
      rd_datin1   : OUT STD_LOGIC;                    -- Signal de lecture d'un nouvel octet applicatif
      new_frame1  : IN STD_LOGIC;                     -- 1 Pulse indique qu'une nouvelle trame est disponible
      l7_ok1      : IN STD_LOGIC;                     -- 1 Pulse indique que la nouvelle trame est conforme du point de vue layer 7
      l7_overflow1: IN STD_LOGIC;                     -- Indique un débordement de la mémoire de frame_store
      com_dispo1  : IN STD_LOGIC;                     -- A 1 tant qu'il y'a des données de commande à traiter dans la DPRAM
 
      -- Interfaces vers le module frame_store2
      datin2      : IN STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Données utiles de la couche applicative (commande, @, data)
      socin2      : IN STD_LOGIC;                     -- Indique que l'octet sur datin2 est le 1er d'une commande
      rd_datin2   : OUT STD_LOGIC;                    -- Signal de lecture d'un nouvel octet applicatif
      new_frame2  : IN STD_LOGIC;                     -- 1 Pulse indique qu'une nouvelle trame est disponible
      l7_ok2      : IN STD_LOGIC;                     -- 1 Pulse indique que la nouvelle trame est conforme du point de vue layer 7
      l7_overflow2: IN STD_LOGIC;                     -- Indique un débordement de la mémoire de frame_store
      com_dispo2  : IN STD_LOGIC;                     -- A 1 tant qu'il y'a des données de commande à traiter dans la DPRAM
 
      -- Interfaces ver le module layer2_tx
      datsent     : OUT  STD_LOGIC_VECTOR(7 DOWNTO 0); -- Flux de données applicatives à émettre
                                                       -- La première donnée du flux qui suit le sof est l'@ de destination
      valsent     : OUT  STD_LOGIC;                    -- validant du bus datsent
      sof         : OUT  STD_LOGIC;                    -- Indique le début d'une trame à émettre
      eof         : OUT STD_LOGIC;                     -- Indique le dernier octet de la trame à émettre
      datsent_free: IN  STD_LOGIC;                     -- Indique que le module layer2_tx a pris en compte l'octet datsent
      clr_fifo_tx : OUT STD_LOGIC;                     -- Signal de purge de la FIFO Tx
 
      -- Interface vers les modules switch
      copy_ena1   : OUT STD_LOGIC;                     -- Autorise la copie de Rx1 sur Tx2
      copy_ena2   : OUT STD_LOGIC;                     -- Autorise la copie de Rx2 sur Tx1
 
      -- Interface de pilotage du module SPI
      reload_fpgan: OUT STD_LOGIC;                     -- Ordre de reconfiguration du FPGA
      spitx_dat   : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Data + commande à sérialiser
      spitx_val   : OUT STD_LOGIC;                     -- Validant de spitx_dat
      spirx_dat   : IN  STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Data reçue
      spirx_val   : IN STD_LOGIC;                      -- Indique des données dispo dans spirx_val
      spirx_next  : OUT STD_LOGIC;                     -- Lit un octet de plus dans spirx_val
      spi_typecom : OUT STD_LOGIC;                     -- Type de commande à éxécuter
      spi_execcom : OUT STD_LOGIC;                     -- Ordre d'exécution d'une commande
      spi_busy    : IN  STD_LOGIC;                     -- Indique que le module SPI est occupé
      spi_nbread  : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Nombre d 'octets à lire avec une commande de lecture
      spi_rstn    : OUT STD_LOGIC                      -- Reset du module SPI
      );
END com_exec;
 
ARCHITECTURE rtl OF com_exec IS
   CONSTANT typ_sync     : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"00";  -- Type pour trame de synchronisation
   CONSTANT typ_reqnoseq : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"08";  -- Type pour trame de requête non sécuritaire
   CONSTANT typ_repnoseq : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"80";  -- Type pour trame de réponse non sécuritaire
   CONSTANT typ_atttid   : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"3C";  -- Type pour trame d'attribution de TID
   CONSTANT com_write    : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"01";  -- Commande d'écriture dans une requête non sec
   CONSTANT com_read     : STD_LOGIC_VECTOR(7 DOWNTO 0) := x"02";  -- Commande de lecture dans une requête non sec
 
 
   -- Définition des adresses des registres internes
   CONSTANT adreg_mac   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(0, 7);
   CONSTANT adreg_iid   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(8, 7);
   CONSTANT adreg_typ   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(16, 7);
   CONSTANT adreg_ver   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(17, 7);
   CONSTANT adreg_verpic: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(18, 7);
   CONSTANT adreg_tid   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(19, 7);
   CONSTANT adreg_sid   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(20, 7);
   -- CONSTANT adreg_outrep: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(23, 7);
   CONSTANT adreg_tcyc  : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(24, 7);
   CONSTANT adreg_status: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(25, 7);
   --CONSTANT adreg_cnfrep: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(26, 7);
   --CONSTANT adreg_cnfcyc: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(27, 7);
   CONSTANT adreg_adref : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(28, 7);
   CONSTANT adreg_szref : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(29, 7);
   CONSTANT adreg_toco1 : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(30, 7);
   CONSTANT adreg_toco2 : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(31, 7);
   --CONSTANT adreg_seq   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(32, 7);
   CONSTANT adreg_toac1 : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(33, 7);
   CONSTANT adreg_toac2 : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(34, 7);
   CONSTANT adreg_sync  : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(35, 7);
   CONSTANT adreg_for   : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(36, 7);
   CONSTANT adreg_conf  : STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(37, 7);
   CONSTANT adreg_loadfpga: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(124, 7);
   CONSTANT adreg_spinbr: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(125, 7);
   CONSTANT adreg_spictl: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(126, 7);
   CONSTANT adreg_spidat: STD_LOGIC_VECTOR(6 DOWNTO 0) := CONV_STD_LOGIC_VECTOR(127, 7);
 
   -- Définition des registres internes
   SIGNAL reg_tid       : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_config    : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_status    : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_adref     : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_szref     : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_cpttoco1  : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_cpttoco2  : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_cpttoac1  : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_cpttoac2  : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_cptsync   : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_cptfor    : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_tcyc      : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_spinbr    : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_spictl    : STD_LOGIC_VECTOR(7 DOWNTO 0);
   SIGNAL reg_loadfpga  : STD_LOGIC_VECTOR(7 DOWNTO 0);
 
   -- Signaux de gestion du cycle
   SIGNAL synchro       : STD_LOGIC;               -- 1 Pulse sur réception d'1 trame de synchro valide
   SIGNAL start_cycle   : STD_LOGIC;               -- 1 Pulse indique le début d'un cycle (sur synhcro ou timer interne)
   SIGNAL tc_tcyc       : STD_LOGIC_VECTOR(15 DOWNTO 0); -- Pour calculer reg_tcyc * 100
   SIGNAL mescycle      : STD_LOGIC_VECTOR(16 DOWNTO 0); -- compteur de mesure de périodes de 10us
   SIGNAL mes10us       : STD_LOGIC_vector(10 DOWNTO 0); -- Compteur de période de clk_sys pour mesurer 10us
   SIGNAL synchro_lock  : STD_LOGIC;               -- A 0 tant qu'on a pas reçu une première synchro
   SIGNAL win_syncbefore: STD_LOGIC;               -- Signal de validité d'une syncro en avance (dernier quart d'un cycle de com)
   SIGNAL win_syncafter : STD_LOGIC;               -- Signal de validité d'une synchro en retard (premier quart d'un cycle de com)
   SIGNAL synchro_miss  : STD_LOGIC;               -- 1 Pulse à 1 indique qu'on a pas reçu de trame de synhcro sur une durée de 1.125xTcyc
   SIGNAL cpt_seq       : STD_LOGIC_VECTOR(7 DOWNTO 0); -- Compteur de numéro de cycle de communication en cours
   SIGNAL mem_sync      : STD_LOGIC;               -- Pour mémoriser le fait que le start_cycle est issue d'une trame de synchro
 
   -- Signaux de gestion des monitoring
   SIGNAL mem_activity1 : STD_LOGIC;               -- Pour mémoriser une activité sur le port 1 entre 2 synchros
   SIGNAL mem_activity2 : STD_LOGIC;               -- Pour mémoriser une activité sur le port 2entre 2 synchros
   SIGNAL err_toco      : STD_LOGIC;               -- 1 Pulse indique une trame réçue sur un port mais pas sur l'autre
   SIGNAL voie_toco     : STD_LOGIC;               -- 0 pour err_toco sur le port 1, 1 pour err_toco sur le port 2
 
   -- Signaux d'interprétation et d'exécution des commandes
   SIGNAL datin         : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Données applicatives à traiter (voie 1 ou 2 multiplexée)
   SIGNAL rd_com        : STD_LOGIC;                     -- Ordre de fetch d'un nouvel octet de donnée applicative 
   SIGNAL com_dispo     : STD_LOGIC;                     -- Multiplexage de com_dispo 1 ou 2 selon la voie
   SIGNAL rd_combuf     : STD_LOGIC;                     -- Buffer provisoire pour multiplexer rd_com
   SIGNAL sel_voie      : STD_LOGIC;                     -- A 0 si on traite la voie 1, a 1 si on traite la voie 2
   SIGNAL typ_field     : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Pour mémoriser le type de trame
   SIGNAL seq_field     : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Pour mémoriser le champ SEQ de la trmae de synhcro
   SIGNAL com_field     : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Pour mémoriser le champ COM d'une trame de requête non secu
   --SIGNAL ad_source     : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Pour mémoriser l'adresse de l'expéditeur de la commande
   SIGNAL oldcom1       : STD_LOGIC_vector(9 DOWNTO 0);  -- Pour stocker jusqu'à 3 commandes
   SIGNAL oldcom2       : STD_LOGIC_vector(9 DOWNTO 0);  -- MSB = voie de réception de la commande, MSB - 1 : le registre est libre (0) ou occupé (1)
   SIGNAL oldcom3       : STD_LOGIC_vector(9 DOWNTO 0);  -- 8 LSB : champs TYP de la commande enregistrée 
   SIGNAL store_com     : STD_LOGIC;                     -- 1 pulse pour enregistrer la commande en cours dans un oldcomx
   SIGNAL delete_com    : STD_LOGIC;                     -- 1 pulse pour effacer la commande en cours des oldcomx
   SIGNAL com_recue     : STD_LOGIC;                     -- A 1 si la commande en cours a déjà été enregistrée dans un oldcomx
   SIGNAL synchro_valide: STD_LOGIC;                     -- A 1 si la commande en cours est une synchrone valide
   SIGNAL iid_temp      : STD_LOGIC_vector(63 DOWNTO 0); -- Registre à décalage pour la réception de l'adresse IID
   SIGNAL cpt_byt       : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Compteur d'octets en émission ou réception
   SIGNAL wr            : STD_LOGIC;                     -- Signal d'écriture d'un octet à l'adresse ad_buf
   SIGNAL rd            : STD_LOGIC;                     -- Signal de lecture d'un octet à l'adresse ad_buf
   SIGNAL rd_r          : STD_LOGIC;                     -- Pour retarder le signal rd de 1 cycle
   SIGNAL ad_buf        : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Adresse de lecture ou d'écriture
   SIGNAL wr_int        : STD_LOGIC;                     -- Signal d'écriture si ad_buf pointe sur un registre interne
   SIGNAL rd_int        : STD_LOGIC;                     -- Signal de lecture si ad_buf pointe sur un registre interne
   SIGNAL dataread      : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Multiplexeur pour la lecture
   SIGNAL memadbuf7     : STD_LOGIC;                     -- Pour latcher le MSB du adbus lors d'une lecture
   SIGNAL datawrite     : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Pour latcher les données à écrire
   SIGNAL datint_read   : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Multiplexeur pour la lecture des registres internes
   SIGNAL socin         : STD_LOGIC;                     -- Multiplexage des signaux socinx
   SIGNAL synchro_outwin: STD_LOGIC;                     -- A 1 sur une synchro en dehors des fenêtres autorisées
   -- Signaux de gestion des SYNC absents
   SIGNAL cpt_syncmiss  : STD_LOGIC_VECTOR(7 DOWNTO 0);  -- Compteur de trames de synhcro absentes
 
   -- Machine d'état de gestion du module
   TYPE comexec_type IS (idle_st, rectyp_st, getadsrce_st, attribtid_st, purgesidsync_st, getseq_st, purgecrcsync_st, getadbuf_st, getnr_st, getcom_st, 
                         writebyt_st, startframe_st, sendseq_st, sendcom_st, sendsrce_st, sendadr_st, sendnr_st, senddata_st, 
                         sendstatus_st, endframe_st,purgecom_st);
   SIGNAL fsm_comexec   : comexec_type;
 
 
 
begin
   --------------------------------------------
   -- Affectation des sorties de configuration du module
   --------------------------------------------
   sync_lock <= synchro_lock;    -- Indique que le module est calé sur une synchro
   tid <= reg_tid;               -- Adresse attribuée au MIO à destination des autres modules
   copy_ena1 <= reg_config(0);   -- Autorisation de recopie du port 1 sur le port 2
   copy_ena2 <= reg_config(1);   -- Autorisationd e recopie du port 2 sur le port 1
   clr_fifo_tx <= '0';           -- On n'utilsie pas le clr des FIFO Tx pour l'instant
   spi_typecom <= reg_spictl(0); -- Type de commande sur le port SPI PROM
   spi_execcom <= reg_spictl(3); -- Lance l'exécution d'une commande SPI PROM
   spi_rstn <= reg_spictl(4);    -- Reset du module SPI PROM
   spi_nbread <= reg_spinbr;     -- Nombre d'octets à lire avec une commande lecture en PROM FPGA
 
   --------------------------------------------
   -- Gestion du signal de reprogrammation du FPGA
   --------------------------------------------
   gest_progb : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reload_fpgan <= '1';
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (reg_spictl(4) = '0') THEN
         -- Tant que le reset du module SPI est actif
            reload_fpgan <= '1';             -- On fait rien
         ELSIF (reg_loadfpga = x"31") THEN
         -- Si le module SPI est actif et qu'on trouve la valeur magique dan le registre
            reload_fpgan <= '0';             -- On force la reprogrammation
         END IF;
      END IF;
   END PROCESS;
 
   --------------------------------------------
   -- Gestion des bus d'accès mémoire 
   --    interne (module SPI inclus)
   --    externe
   --------------------------------------------
   ad_out <= ad_buf(6 DOWNTO 0);    -- Bus d'accès aux registres externes (7 bits de long)
   datout_write <= datawrite;       -- Donnée à écrire dans un registre externe
   wr_int <= wr AND NOT(ad_buf(7)); -- On écrit un registre interne selon le MSB de l'@
   -- On lit un registre interne selon le MSB de l'@
   rd_int <= rd AND datsent_free AND NOT(ad_buf(7));  -- On ne lit rien si le module destination n'est pas prêt
   wr_out <= wr AND ad_buf(7);      -- On écrit un registre externe selon le MSB de l'@
   -- On lit un registre externe selon le MSB de l'@
   rd_out <= rd AND datsent_free AND ad_buf(7);       -- On ne lit rien si le module destination n'est pas prêt 
 
   -- On écrit dans la FIFO de commande SPI si signal d'écriture à l'adresse de la FIFO
   spitx_val <= wr_int WHEN (ad_buf(6 DOWNTO 0) =  adreg_spidat) ELSE '0';
   spitx_dat <= datawrite;          -- la donnée est directement sur le bus d'écriture (passe pas par un regditre)
 
   -- On fetche un octet de plus dans la FIFO de lecture SPI si signal de lecture à l'adresse de la FIFO
   spirx_next<= rd_int WHEN (ad_buf(6 DOWNTO 0) =  adreg_spidat) ELSE '0';   
 
   --------------------------------------------
   -- Gestion de la lecture sur le bus interne
   --------------------------------------------
   gest_readint : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         datint_read <= (others => '0');
         memadbuf7 <= '0';
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1') THEN
            CASE ad_buf(6 DOWNTO 0) IS
            -- Décodage du registre lu en focntion de l'adresse du registre
               WHEN adreg_mac   | adreg_mac+1 | adreg_mac+2 | adreg_mac+3 |
                    adreg_mac+4 | adreg_mac+5 | adreg_mac+6 | adreg_mac+7 => datint_read <= x"FF";
               WHEN adreg_iid =>    datint_read <= iid( 7 DOWNTO  0);
               WHEN adreg_iid+1 =>  datint_read <= iid(15 DOWNTO  8);  
               WHEN adreg_iid+2 =>  datint_read <= iid(23 DOWNTO 16);
               WHEN adreg_iid+3 =>  datint_read <= iid(31 DOWNTO 24);
               WHEN adreg_iid+4 =>  datint_read <= iid(39 DOWNTO 32);
               WHEN adreg_iid+5 =>  datint_read <= iid(47 DOWNTO 40);
               WHEN adreg_iid+6 =>  datint_read <= iid(55 DOWNTO 48);
               WHEN adreg_iid+7 =>  datint_read <= iid(63 DOWNTO 56);
               WHEN adreg_typ =>    datint_read <= reg_typemio;
               WHEN adreg_ver =>    datint_read <= reg_version;
					WHEN adreg_verpic => datint_read <= x"00";
               WHEN adreg_tid =>    datint_read <= reg_tid;
               WHEN adreg_sid   | adreg_sid+1 | adreg_sid+2 => datint_read <= x"FF";
               WHEN adreg_tcyc =>   datint_read <= reg_tcyc;
               WHEN adreg_status => datint_read <= reg_status;
               WHEN adreg_adref =>  datint_read <= reg_adref;
               WHEN adreg_szref =>  datint_read <= reg_szref;
               WHEN adreg_toco1 =>  datint_read <= reg_cpttoco1;
               WHEN adreg_toco2 =>  datint_read <= reg_cpttoco2;
               WHEN adreg_toac1 =>  datint_read <= reg_cpttoac1;
               WHEN adreg_toac2 =>  datint_read <= reg_cpttoac2;
               WHEN adreg_sync =>   datint_read <= reg_cptsync;
               WHEN adreg_for =>    datint_read <= reg_cptfor;
               WHEN adreg_conf =>   datint_read <= reg_config;
               WHEN adreg_spinbr => datint_read <= reg_spinbr;
               WHEN adreg_spictl => datint_read <= spirx_val & reg_spictl(6 DOWNTO 4) & spi_busy & reg_spictl(2 DOWNTO 0);
               WHEN adreg_spidat => datint_read <= spirx_dat;
               WHEN OTHERS =>       datint_read <= reg_status;
            END CASE;
         END IF;
         IF (rd = '1') THEN
         -- sur une lecture, que ce soit interne ou externe
            memadbuf7 <= ad_buf(7);       -- On mémrosie quelle zone mémoire était lue
         END IF;
      END IF;
   END PROCESS;
   -- Lorsqu'on lit une donnée, on multiplexe soit le bus externe, soit les registres internes selon le MSB de l'adresse de lecture
   dataread <= datout_read WHEN memadbuf7 = '1' ELSE datint_read;
 
   --------------------------------------------
   -- Gestion des écritures dans les registres internes de configuration
   -- L'écriture dans les registres internes de status est gérée au cas par cas.
   --------------------------------------------
   gest_wrreg : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reg_config <= x"03";
         reg_adref  <= ad_ref;
         reg_szref  <= sz_ref;
         reg_tcyc   <= x"FF";
         reg_spinbr   <= x"00";
         reg_loadfpga <= x"00";
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (wr_int = '1') THEN
            CASE ad_buf(6 DOWNTO 0) IS
            -- Mise à jour du registre en focntion du décodage de l'adresse du registre
               WHEN adreg_conf =>
                  reg_config <= datawrite;
               WHEN adreg_adref =>
                  reg_adref <= datawrite;
               WHEN adreg_szref =>
                  reg_szref <= datawrite;
               WHEN adreg_tcyc =>
                  reg_tcyc <=  datawrite;
               WHEN adreg_loadfpga =>
                  IF (reg_spictl(4) = '1') THEN
                  -- On autorise la mise à jour du registre que si le reste SPI est relaché
                     reg_loadfpga <= datawrite;
                  END IF;
               WHEN adreg_spinbr =>
                  reg_spinbr <= datawrite;
               WHEN OTHERS =>
                  NULL;
            END CASE;
         END IF;
         IF (wr_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_spictl) THEN     
            reg_spictl <= datawrite;
         ELSE
         -- On s'assure que le pulse de commande du SPI ne dure qu'un seul cycle
            reg_spictl(3) <= '0';
         END IF;
      END IF;
   END PROCESS;
 
   ----------------------------------
   -- Gestion du registre de status
   ----------------------------------
   gest_stat : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
      -- Par défaut le bit REP est à 1
         reg_status <= x"80";
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_status) OR 
            (fsm_comexec = sendstatus_st AND datsent_free = '1') THEN
         -- Le registre status est resetté à chaque lecture
            reg_status <= x"00";
         ELSE
            IF (err_toco = '1') THEN
            -- Gestion du bit CNR (trame non reçue sur un des ports)
               reg_status(0) <= '1';
            END IF;
            -- Bit ESE non implémenté
            IF (start_cycle = '1' AND mem_activity1 = '0') THEN
            -- Gestion du bit NA1 (pas d'activité sur le port 1)
               reg_status(2) <= '1';
            END IF;
            IF (start_cycle = '1' AND mem_activity2 = '0') THEN
            -- Gestion du bit NA2 (pas d'activité sur le port 2)
               reg_status(3) <= '1';
            END IF;
            IF (synchro_miss = '1') THEN
            -- Gestion du bit NTS (pas de trame de synhcro)
               reg_status(4) <= '1';
            END IF;
            IF (new_frame1 = '1' AND l7_ok1 = '0') OR
               (new_frame2 = '1' AND l7_ok2 = '0') THEN
            -- Gestion du bit BFO (format de trame non supporté)
               reg_status(5) <= '1';
            END IF;
            reg_status(6) <= sel_voie;
            reg_status(7) <= '0';
         END IF;
      END IF;
   END PROCESS;
 
   --------------------------------------------
   -- Gestion de la synchro
   --------------------------------------------
   gest_sync : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         cpt_syncmiss <= (others => '0');
         synchro_lock <= '0';
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (synchro_outwin = '1' AND synchro_lock = '1') THEN
         -- Sur synchro en dehors de la fenêtre alors qu'on est déjà locké
            cpt_syncmiss <= (others => '0');    -- On annule le compteur de trame de sync manquante
            synchro_lock <= '0';                -- On se délocke de la synchro d'avant
         ELSE
            IF (synchro = '1') THEN
            -- Sur chaque trame de synchro traitée
               synchro_lock <= '1';
               cpt_syncmiss <= (others => '0');   
            ELSIF (synchro_miss = '1') THEN
            -- Sur chaque cycle sans trame de synchro
               IF (cpt_syncmiss /= x"FF") THEN
               -- On comtpe de 0 à 255 et on s'arrêt de compter
                  cpt_syncmiss <= cpt_syncmiss + 1;
               ELSE
                  synchro_lock <= '0';          -- On considère qu'on a perdu la synchro au bout de 255 cycles sans synchro
               END IF;
            END IF;
         END IF;
      END IF;
   END PROCESS;
 
   --------------------------------------------
   -- Gestion du temps de cycle de communication
   --------------------------------------------
   gest_cycle : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         mes10us <= (others => '0');
         mescycle <= CONV_STD_LOGIC_VECTOR(1, mescycle'LENGTH);
         tc_tcyc <= (OTHERS => '0');
         start_cycle <= '0';
         win_syncbefore <= '0';
         win_syncafter <= '0';
         synchro_miss <= '0';
         cpt_seq <= (others => '0');
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         --tc_tcyc <= reg_tcyc * 100;       -- reg_tcyc est en périodes de 1ms mais la mesure se fait en périodes de 10us
         tc_tcyc <= EXT(reg_tcyc & "000000", tc_tcyc'LENGTH) + EXT(reg_tcyc & "00000", tc_tcyc'LENGTH) + EXT(reg_tcyc & "00", tc_tcyc'LENGTH);
         IF (synchro = '1') THEN
         -- Si on a reçu une trame de synchro valide
            mes10us <= (others => '0');            -- Compteur de durée de 10us
            mescycle <= CONV_STD_LOGIC_VECTOR(1, mescycle'LENGTH);   -- Compteur de périodes de 10us (compte de 1 à TCYC)
            start_cycle <= '1';                    -- On indique au module un début de cycle
            win_syncbefore <= '0';                 -- On a plus besoin de la fenêtre de validité de synchro en avance
            win_syncafter <= '1';                  -- Par contre, onpeut recevoir une synchro en retard
            synchro_miss <= '0';                   
            cpt_seq <= seq_field;                  -- Le numéro de cycle est celui de la commande
         ELSE
            IF (mes10us = CONV_STD_LOGIC_VECTOR(freq_clksys*10-1, mes10us'length)) THEN
            -- Toutes les 10 us
               mes10us <= (others => '0');
               IF (mescycle = ('0' & tc_tcyc) + ("0000" & tc_tcyc(tc_tcyc'LEFT DOWNTO 3))) THEN
               -- Si on a mesuré 1.125xTCYC
                  win_syncbefore <= '0';           -- Les fenêtres de validité sont annulées
                  win_syncafter <= '0';
                  -- On remet le compteur à 0.125xTCYC pour mesure 1 TCYC entre 0.125 et 1.125TCYC
                  mescycle <= ("0000" & tc_tcyc(tc_tcyc'LEFT DOWNTO 3)) + 1;
                  synchro_miss <= '1';             -- On indique qu'on a manqué une synchro sur la période précédente
                  start_cycle <= '1';              -- On démarre un cycle sur ordre interne (pas de trame de synchro)
               ELSE
               -- Si on a pas encore atteind le 1.125xTCYC
                  mescycle <= mescycle + 1;
                  IF (mescycle = ('0' & tc_tcyc) - ("0000" & tc_tcyc(tc_tcyc'LEFT DOWNTO 3))) THEN
                  -- Si on est à 0.875x TCYC
                     win_syncbefore <= '1';        -- On active le signal de validité en avance de la synchro
                     win_syncafter <= '0';         -- Par contre la synchro en retard ne serait pas valide
                  ELSIF (mescycle = ('0' & tc_tcyc)) THEN
                  -- Si on est pile à TCYC
                     win_syncbefore <= '0';
                     win_syncafter <= '1';         -- On active le signal de validité en retard de la synchro
                     cpt_seq <= cpt_seq + 1;       -- On est dans le numéro de cycle suivant
                  ELSIF (mescycle = ("0000" & tc_tcyc(tc_tcyc'LEFT DOWNTO 3))) THEN
                  -- Si on est pile à 0.125xTCYC
                     win_syncafter <= '0';         -- On annule le signal de validité en retard de la synchro
                 END IF;
               END IF;
            ELSE
            -- pour tous les autres états dans le cycle
               start_cycle <= '0';                 -- On assure que les signaux ne durent qu'un clk
               synchro_miss <= '0';
               mes10us <= mes10us + 1;
            END IF;
         END IF;
      END IF;
   END PROCESS;
 
   --------------------------------------------
   -- Gestion des compteurs de monitoring
   --------------------------------------------
   -- Compteur de trames avec un mauvais format (champs invalides, CRC faux, ...)
   gest_for : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reg_cptfor <= (others => '0');   
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_for) THEN
         -- Le registre est remis à 0 ou à 1 lors d'une lecture selon la condition de génération
            IF (new_frame1 = '1' AND l7_ok1 = '0') OR (new_frame2 = '1' AND l7_ok2 = '0') THEN
               reg_cptfor(0) <= '1';
            ELSE
               reg_cptfor(0) <= '0';
            END IF;
            reg_cptfor(7 DOWNTO 1) <= "0000000";
         ELSIF (reg_cptfor /= x"FF") THEN
            IF (new_frame1 = '1' AND l7_ok1 = '0') OR
               (new_frame2 = '1' AND l7_ok2 = '0') THEN
            -- Sur réception d'une nouvelle trame avec un mauvais format quel que soit le port
               reg_cptfor <= reg_cptfor + 1;
            END IF;
         END IF;
      END IF;
   END PROCESS;
 
   -- Compteur d'absence de trame de synchro
   gest_abssync : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reg_cptsync <= (others => '0');   
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_sync) THEN
         -- Le registre est remis à 0 ou à 1 lors d'une lecture selon la condition de génération
            reg_cptsync <= "0000000" & synchro_miss;
         ELSIF (reg_cptsync /= x"FF") THEN
            IF (synchro_miss = '1') THEN
               reg_cptsync <= reg_cptsync + 1;
            END IF;
         END IF;
      END IF;
   END PROCESS;
 
   -- Compteur de cycles sans activité sur le port 1
   gest_toac1 : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reg_cpttoac1 <= (others => '0');
         mem_activity1 <= '1';         
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_toac1) THEN
         -- Le registre est remis à 0 ou à 1 lors d'une lecture selon la condition de génération         
            IF (start_cycle = '1' AND mem_activity1 = '0') THEN
               reg_cpttoac1(0) <= '1';
            ELSE
               reg_cpttoac1(0) <= '0';
            END IF;
            reg_cpttoac1(7 DOWNTO 1) <= "0000000";
         ELSIF (reg_cpttoac1 /= x"FF") THEN
            IF (start_cycle = '1' AND mem_activity1 = '0') THEN
            -- A chaque début de cycle, si on a pas eu du tout d'activité
               reg_cpttoac1 <= reg_cpttoac1 + 1;
            END IF;
         END IF;
         IF (activity1 = '1') THEN
         -- Bascule JK de mémorisation d'activité
            mem_activity1 <= '1';
         ELSIF (start_cycle = '1') THEN
         -- Remise à 0 à chaque début de cycle
            mem_activity1 <= '0';
         END IF; 
      END IF;
   END PROCESS;
 
   -- Compteur de cycles sans activité sur le port 2
   gest_toac2 : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reg_cpttoac2 <= (others => '0');
         mem_activity2 <= '1';         
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_toac2) THEN
         -- Le registre est remis à 0 ou à 1 lors d'une lecture selon la condition de génération         
            IF (start_cycle = '1' AND mem_activity2 = '0') THEN
               reg_cpttoac2(0) <= '1';
            ELSE
               reg_cpttoac2(0) <= '0';
            END IF;
            reg_cpttoac2(7 DOWNTO 1) <= "0000000";
         ELSIF (reg_cpttoac2 /= x"FF") THEN
            IF (start_cycle = '1' AND mem_activity2 = '0') THEN
               reg_cpttoac2 <= reg_cpttoac2 + 1;
            END IF;
         END IF;
         IF (activity2 = '1') THEN
            mem_activity2 <= '1';
         ELSIF (start_cycle = '1') THEN
            mem_activity2 <= '0';
         END IF; 
      END IF;
   END PROCESS;
 
   -- Compteur de trames reçues sur le port 2 mais pas sur le port 1 durant le cycle
   gest_toco1 : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reg_cpttoco1 <= (others => '0');
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_toco1) THEN
         -- Sur lecture du registre, remise à 0 ou à 1 du compteur selon la condition de généraltion de l'évènement
            IF (err_toco = '1' AND voie_toco = '0') THEN
               reg_cpttoco1(0) <= '1';
            ELSE
               reg_cpttoco1(0) <= '0';
            END IF;
            reg_cpttoco1(7 DOWNTO 1) <= "0000000";
         ELSIF (reg_cpttoco1 /= x"FF") THEN
            IF (err_toco = '1' AND voie_toco = '0') THEN
            -- Si on a une trame absente (err_toco) sur le voie 1 (voie_toco)
               reg_cpttoco1 <= reg_cpttoco1 + 1;
            END IF;
         END IF;
      END IF;
   END PROCESS;
 
   -- Compteur de trames reçues sur le port 1 mais pas sur le port 2 durant le cycle
   gest_toco2 : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         reg_cpttoco2 <= (others => '0');
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (rd_int = '1' AND ad_buf(6 DOWNTO 0) = adreg_toco2) THEN
            IF (err_toco = '1' AND voie_toco = '1') THEN
               reg_cpttoco2(0) <= '1';
            ELSE
               reg_cpttoco2(0) <= '0';
            END IF;
            reg_cpttoco2(7 DOWNTO 1) <= "0000000";
         ELSIF (reg_cpttoco2 /= x"FF") THEN
            IF (err_toco = '1' AND voie_toco = '1') THEN
            -- Si on a une trame absente (err_toco) sur le voie 2 (voie_toco)
               reg_cpttoco2 <= reg_cpttoco2 + 1;
            END IF;
         END IF;
      END IF;
   END PROCESS;
 
   --------------------------------------------
   -- multiplexage des données selon si on traite la voie 1 ou la voie 2
   --------------------------------------------
   datin <= datin1 WHEN (sel_voie = '0') ELSE datin2;    -- Sélection de la donnée à traiter
   socin <= socin1 WHEN (sel_voie = '0') ELSE socin2;    -- Sélection du signal soc selon la voie
   rd_datin1 <= rd_combuf WHEN (sel_voie = '0') ELSE '0';-- On lit un octet de plus dans le bon buffer
   rd_datin2 <= rd_combuf WHEN (sel_voie = '1') ELSE '0';
   com_dispo <= com_dispo1 WHEN (sel_voie = '0') ELSE com_dispo2; -- Il y'a une commande dispo sur la voie sélectionnée
 
   -- Condition de lecture dans une méoire de commande. Si on est en purge, on lit jusqu'à la trame suivante 
   -- ou bien que la méoire soit vide
   rd_combuf <= rd_com AND com_dispo AND NOT(socin) WHEN (fsm_comexec = purgecom_st) ELSE 
                rd_com AND com_dispo;
 
   --------------------------------------------
   -- Mémorisation des commandes déjà reçues dans le cycle
   -- Format des oldcom
   --    MSB : Voie où la commande a été reçue
   --  MSB-1 : Le oldcom est occupé (une commande est mémorisée)
   --  8 LSB : champs de la commande
   -- Les oldcom sont gérés comme une FIFO (oldcom1 est le premier remplit)
   --------------------------------------------
   mem_oldcom : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         oldcom1 <= (others => '0');
         oldcom2 <= (others => '0');
         oldcom3 <= (others => '0');
         err_toco <= '0'; 
         voie_toco <= '0';
         mem_sync <= '0';
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         IF (synchro = '1') THEN
         -- mem_sync est une bascule JK de mémorisation de réception d'une trame de synchro dans le cycle en cours
            mem_sync <= '1';
         ELSIF (start_cycle = '1') THEN
         -- A chaque début de cycle (qu'il soit sur réception de synchro ou timer)
            mem_sync <= '0';
         END IF;
         IF (start_cycle = '1') THEN
         -- Au début d'un cycle
            IF (mem_sync = '1') THEN
            -- Si ce cycle a été induit par une trame de synchro
            -- On utilise typ_sync (en dur) au lieu de typ_field car typ_field est écrasé pour la trame réflexe
               oldcom1 <= sel_voie & '1' & typ_sync; -- On mémorise la commande
               oldcom2(8) <= '0';         -- Au début d'un cycle initié par une trame de synchro
               oldcom3(8) <= '0';         -- On purge les FIFO oldcom
            END IF;
            IF (oldcom1(8) = '1') THEN 
            -- Si la première case mémoire n'était pas vide
               err_toco <= '1';              -- Ca veut dire qu'une commande d'une cycle précédent n'a pas eu sa jumelle 
               voie_toco <= NOT(oldcom1(9)); -- On va incrémenter le compteur opposé au port
            ELSE
               err_toco <= '0';
            END IF;
         ELSIF (store_com = '1') THEN
         -- Sur réception d'un ordre de stockage de la commande, on cherche la première case vide
            IF (oldcom1(8) = '0') THEN
               oldcom1 <= sel_voie & '1' & typ_field;
            ELSIF (oldcom2(8) = '0') THEN
               oldcom2 <= sel_voie & '1' & typ_field;
            ELSIF (oldcom3(8) = '0') THEN
               oldcom3 <= sel_voie & '1' & typ_field;
            ELSE
            -- Si pas de case vide, on jette la plus vieille et on stoke à la fin de la file
               oldcom1 <= oldcom2;
               oldcom2 <= oldcom3;
               oldcom3 <= sel_voie & '1' & typ_field;
            END IF;
            err_toco <= '0';
         ELSIF (delete_com = '1') THEN
         -- Sur réception d'un ordre d'effacement (i.e. la commande en cours a déjà été reçue, donc on l'efface de la mémoire)
            IF (oldcom1 = NOT(sel_voie) & '1' & typ_field) THEN
            -- En principe, il faut que ce soit la première disponible car elles arrivent forcément dans le même ordre sur les 2 ports
               oldcom1 <= oldcom2;              -- On efface la première en recopiant la 2ème et la 3ème
               oldcom2 <= oldcom3;     
               oldcom3(8) <= '0';               -- On vide la 3ème case
               err_toco <= '0';                           
            ELSIF (oldcom2 = NOT(sel_voie) & '1' & typ_field) THEN
            -- Si la commande en cours est stockée en 2ème position, ça veut dire que la commande stockée
            -- en première position a été perdue sur l'autre port
               oldcom1 <= oldcom3;              -- On jette les 2 premières mémoires
               oldcom2(8) <= '0';
               oldcom3(8) <= '0';
               err_toco <= '1';                 -- On indique l'erreur   
               voie_toco <= NOT(oldcom1(9));    -- et sur quelle voie il y'a eu l'erreur
            ELSIF (oldcom3 = NOT(sel_voie) & '1' & typ_field) THEN
            -- Si la commande en cours est stockée en 3ème position, ça veut dire que les commandes stockées
            -- en première et deuxième position ont été perdues sur l'autre port. On a donc 2 erreurs à enregistrer
            -- On purge la mémoire 1 et 3 et on indiquee une erreur. On garde la mémoire 2 en mémoire 1 pour traiter l'erreur
            -- à la prochaine commande ou au début du cycle suivant
               oldcom1 <= oldcom2;              -- On purge donc toutes les mémoires
               oldcom2(8) <= '0';
               oldcom3(8) <= '0';
               err_toco <= '1';                 -- On indique l'erreur               
               voie_toco <= NOT(oldcom1(9));    -- et sur quelle voie il y'a eu l'erreur
            END IF;
         ELSE
            err_toco <= '0';                    -- Pour assurer qu'il ne dure qu'un seul cycle
         END IF;
      END IF;
   END PROCESS;
 
   --------------------------------------------
   -- Machine d'état de gestion du module
   --------------------------------------------
   -- Une commande est déjà reçue sur l'autre port si elle dans une des 3 cases mémoires
   com_recue <= '1' WHEN ((oldcom1 = NOT(sel_voie) & '1' & datin) OR
                          (oldcom2 = NOT(sel_voie) & '1' & datin) OR
                          (oldcom3 = NOT(sel_voie) & '1' & datin)) ELSE '0';   
 
   -- Une trame de synchro est valide si c'est la première (synchro_lock = '0') ou bien si elle est
   -- dans la fenêtre de validité en avance avec SEQ = cpt_seq+1 ou bien si elle est dans la
   -- fenêtre de validité en retard avec SEQ = cpt_seq
   synchro_valide <= '1' WHEN (typ_field = typ_sync AND 
                              (synchro_lock = '0' OR
                              (win_syncbefore = '1' AND datin = cpt_seq + 1) OR
                              (win_syncafter = '1' AND datin = cpt_seq))) ELSE '0';
 
   man_fsm : PROCESS(clk_sys, rst_n)
   BEGIN
      IF (rst_n = '0') THEN
         fsm_comexec <= idle_st;
         sel_voie <= '1';
         cpt_byt <= (others => '0');
         reg_tid <= x"8F";
         wr <= '0';
         rd <= '0';
         rd_r <= '0';
         ad_buf <= (others => '0');
         --ad_source <= (others => '0');     -- Inutilisé car les TID sdestination sont en dur (multicast)
         store_com <= '0';
         delete_com <= '0';
         synchro_outwin <= '0';
         rd_com <= '0';
         sof <= '0';
         eof <= '0';
         datsent <= (others => '0');
         valsent <= '0';
         datawrite <= (others => '0');
         synchro <= '0';
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
         CASE fsm_comexec IS
            WHEN idle_st =>
            -- Etat d'attente de données disponibles dans les modules frame_ana
               wr <= '0';
               rd <= '0';
               synchro <= '0';
               store_com <= '0';
               delete_com <= '0';
               synchro_outwin <= '0';
               sof <= '0';
               eof <= '0';
               valsent <= '0';
               IF (com_dispo1 = '1' AND (com_dispo2 = '0' OR sel_voie = '1')) THEN
               -- On s'assure qu'on traite par alternance une voie et puis l'autre. On traite la voie 1 que si y'a
               -- des données a traiter et que soit la voie 2 est vide soit on a traité la voie 2 au coup d'avant
                  sel_voie <= '0';              -- Sélection de la voie de traitement
                  rd_com <= '1';                -- On demande un octet de plus par anticipation
                  fsm_comexec <= rectyp_st;
               ELSIF (com_dispo2 = '1') THEN
               -- Si on des données dans la voie 2
                  sel_voie <= '1';              -- Sélection de la voie à traiter
                  rd_com <= '1';
                  fsm_comexec <= rectyp_st;
               ELSE
                  rd_com <= '0';
               END IF;
 
            WHEN rectyp_st =>
            -- Etat d'enregistrement du type de commande
               typ_field <= datin;    -- Mémorise la partie typ
               IF (com_recue = '0') THEN
               -- Si on a pas déjà reçu cette com dans ce cycle
                  fsm_comexec <= getadsrce_st;     -- 
                  IF (datin /= typ_sync) THEN
                  -- Si c'est pas une synchro, on l'enregistre. La synchro est mémorisée lors du start_cycle
                     store_com <= '1';
                  END IF;
               ELSE
               -- Si on a déjà reçu cette commande
                  delete_com <= '1';         -- On efface la commande des mémoires
                  fsm_comexec <= purgecom_st;-- On va purger la mémoire sans faire l'action associée
               END IF;
 
            WHEN getadsrce_st =>  
            -- Etat de récupération de l'adresse source de la commande
               --ad_source <= datin;
               store_com <= '0';
               delete_com <= '0';
               -- On va traiter la commande en fonction de son type
               IF (typ_field = typ_sync) THEN
               -- Si c'est une trame de synchro
                  cpt_byt <= x"01";
                  fsm_comexec <= purgesidsync_st;  -- On va purger le SID de la trame de synhcro
               ELSIF (typ_field = typ_atttid) THEN
               -- Si c'est une commande type attribution de TID
                  cpt_byt <= x"08";             -- On se prépare à recevoir l'@ IID (8 octets)
                  fsm_comexec <= attribtid_st;
               ELSIF (typ_field = typ_reqnoseq) THEN
               -- Si c'est une commande non sécu
                  fsm_comexec <= getcom_st;
               ELSE
               -- Si c'est une trame qu'on ne sait pas traiter, on va la purger
                  rd_com <= '0';
                  fsm_comexec <= purgecom_st;
               END IF;
 
            WHEN purgesidsync_st =>
            -- Etat d'attente de récupération du SID de la trame de synhcro (on n'en fait rien)
               cpt_byt <= cpt_byt - 1;
               IF (cpt_byt = "00") THEN
                  fsm_comexec <= getseq_st;
               END IF;
 
            WHEN getseq_st =>
            -- Etat de réception du compteur de séquence de la trame de SYNC
               seq_field <= datin;
               IF (synchro_valide = '1') THEN
               -- Si le champ SEQ est valable
                  rd_com <= '1';                -- On continu de lire pour supprimer le CRC
                  synchro <= '1';               -- On signale une synchro valide
                  IF (reg_config(3) = '1') THEN
                  -- Si on est autorisé à émettre la trame de réponse réflexe
                  -- On va d'abord purger les 2 CRC de la commande
                     fsm_comexec <= purgecrcsync_st;
                     cpt_byt <= x"01";
                  ELSE
                  -- Si on n'est pas autorisé à émettre la trame réflexe
                     fsm_comexec <= purgecom_st;   -- On va purger la commande
                  END IF;
               ELSE
                  IF (win_syncbefore = '0' AND win_syncafter = '0') THEN
                  -- Si on a reçu une trame de synchro en dehors des fenêtres de validité
                     synchro_outwin <= '1';
                  END IF;               
                  fsm_comexec <= purgecom_st;
                  rd_com <= '0';
               END IF;
 
            WHEN purgecrcsync_st =>
            -- Etat de purge des 2 octets de CRC de la trmae de synchro
               synchro <= '0';
               cpt_byt <= cpt_byt - 1;
               IF (cpt_byt = "00") THEN
                  -- Si on a fini de purger le CRC de la trame de synchro
                  fsm_comexec <= startframe_st; -- On va émettre une trame
                  sof <= '1';
                  datsent <= x"F0";             -- Le 1er octet à envoyer est l'adresse de destination (i.e multicast concentrateur)
                  valsent <= '1';               -- On valide l'octet sorti
                  ad_buf <= reg_adref;          -- l'@ des données à émettre est donnée par ce registre
                  cpt_byt <= reg_szref;         -- le nombre d'octets à émettre est donné par ce registre
                  rd_com <= '0';
               END IF;
 
            WHEN attribtid_st =>
            -- Etat d'exécution d'une trame d'attribution de TID
               iid_temp <= datin & iid_temp(63 DOWNTO 8);   -- On récupère l'@ IID octet par octet
               cpt_byt <= cpt_byt - 1;
               IF (cpt_byt = x"00") THEN
               -- Si on reçu les 8 octets
                  IF (iid_temp = iid) THEN
                  -- Si l@ IID reçue correspond à l'@ IID du module
                     reg_tid <= datin;                    -- On mémorise le nouveau TID
                  END IF;
                  fsm_comexec <= idle_st;
                  rd_com <= '0';
               END IF;
 
            WHEN getcom_st =>
            -- Etat de récupération de l'octet de commande de la trame de requête non sécu
               com_field <= datin;
               rd_com <= '1';
               fsm_comexec <= getnr_st;
 
            WHEN getnr_st =>
            -- Etat de récupération du nombre d'octets à traiter dans la requête, que ce soit une écriture ou une lecture
               cpt_byt <= datin;
               rd_com <= '1';
               fsm_comexec <= getadbuf_st;
 
            WHEN getadbuf_st =>
            -- Etat de récupération de l'adresse où lire ou écrire
               ad_buf <= datin;
               IF (com_field = com_write) THEN
               -- Si c'est une commande d'écriture
                  fsm_comexec <= writebyt_st;
                  rd_com <= '1';
               ELSIF (com_field = com_read) THEN
               -- Si c'est une commande de lecture
                  fsm_comexec <= startframe_st; -- On va émettre une trame
                  sof <= '1';                   -- On signale au module layer2_Tx qu'on veut émettre une trame
                  datsent <= x"F0";             -- Le 1er octet à envoyer est l'adresse de destination (i.e multicast concentrateur)
                  valsent <= '1';               -- On valide l'octet sorti
                  rd_com <= '0';                -- On ne lit plus rien de la commande pour l'instant
               ELSE               -- Si c'est autre chose, il y'a eu un problème de desynchronisation -> on va purger la mémoire de commande
                  fsm_comexec <= purgecom_st;
                  rd_com <= '0';
               END IF;
 
            WHEN writebyt_st =>
            -- Etat d'écriture d'une séquence d'octets
               datawrite <= datin;
               wr <= '1';
               cpt_byt <= cpt_byt - 1 ;
               IF (wr = '1' AND (ad_buf /= ('0' & adreg_spidat)) AND ad_buf /= x"FF") THEN            
               -- on n'incrémente l'adresse que si le WR a été effectué
               -- Si on accède à la FIFO SPI, le registre d'addresse reste bloqué à cette valeur
                  ad_buf <= ad_buf + 1;
               END IF;
               IF (cpt_byt = x"01") THEN
               -- Si on a traité tous les octets
                  rd_com <= '0';             -- On arrête de lire dans la commande
                  fsm_comexec <= idle_st;    -- Mais le signal de wr reste actif un cycle de plus (remis à 0 dans idle)
               END IF;
 
            WHEN startframe_st =>
               -- Prépare l'envoie d'une trame
               synchro <= '0';   -- Le signal de synhcro ne doit durer qu'1 pulse
               IF (datsent_free = '1') THEN
               -- Si le 1er octet est en cours de traitement, l'octet suivant est le TYPE
                  sof <= '0';             -- Le sof a été pris en compte
                  datsent <= x"80";       -- Type de réponse non sécu
                  valsent <= '1';
                  fsm_comexec <= sendsrce_st;
               END IF;
 
            WHEN sendsrce_st =>
            -- Envoie l'@ source (i.e l'@ du MIO) de la trame à émettre
               IF (datsent_free = '1') THEN
                  datsent <= reg_tid;   
                  valsent <= '1';
                  fsm_comexec <= sendcom_st;
               END IF;
 
            WHEN sendcom_st =>
            -- Envoie du numéro de séquence courante
               IF (datsent_free = '1') THEN
                  IF (typ_field = typ_sync) THEN
                  -- Si on répond à une trame de synchro
                     datsent <= x"01";
                  ELSE
                  -- Si on répond à une trame de requête
                     datsent <= x"02";
                  END IF;
                  valsent <= '1';
                  fsm_comexec <= sendnr_st;
               END IF;
 
            WHEN sendnr_st =>
            -- Envoie du nombre d 'octets lus
               IF (datsent_free = '1') THEN
                  datsent <= cpt_byt;   
                  valsent <= '1';
                  fsm_comexec <= sendadr_st;
               END IF;
 
            WHEN sendadr_st =>
            -- Envoie de l'adresse ou on a lu les octets
               IF (datsent_free = '1') THEN
                  datsent <= ad_buf;   
                  rd <= '1';
                  rd_r <= '0';
                  valsent <= '1';
                  fsm_comexec <= senddata_st;
               END IF;
 
            WHEN senddata_st =>
            -- Envoie les octets lus. rd_r est utilisé pour signaler que la donnée en lecture est disponible.
            -- Il est décalé de 1 cycle par rappott à rd 
               IF (rd = '1' AND datsent_free = '1') THEN
                  rd_r <= '1';
                  IF ((ad_buf /= ('0' & adreg_spidat)) AND ad_buf /= x"FF") THEN
                  -- Si on accède à la FIFO rx du SPI, on reste calé sur cette adresse
                     ad_buf <= ad_buf + 1;
                  END IF;
                  cpt_byt <= cpt_byt - 1;
               END IF;
               IF (datsent_free = '1') THEN
               -- Si le module layer2_tx est dispo pour prendre la donnée
                  IF (cpt_byt = x"01") THEN
                     rd <= '0';                       -- Il ne faut plus lire en mémoire
                  END IF;
                  IF (rd_r = '1') THEN
                  -- Si le module layer2_tx est dispo et qu'une donnée est dispo
                     datsent <= dataread;         -- On l'envoie
                     valsent <= '1';
                     IF (cpt_byt = x"00") THEN
                     -- Si c'est la dernière donnée
                        IF (typ_field = typ_sync) THEN
                        -- Si on répond à une trame de synchro
                           fsm_comexec <= sendstatus_st; -- Il faut envoyer l'octet de status
                        ELSE
                        -- Si c'est une requête normale
                           fsm_comexec <= endframe_st; -- On a fini
                           eof <= '1';
                        END IF;
                     END IF;
                  ELSE
                  -- S'il n'y a pas dedonnée prête (cas se présente quand on rentre dans cet état et qu'on a pas encore lu)  
                     valsent <= '0';         -- On indique au module layer2_tx qu'il n'y a pas de nouvelle donnée
                  END IF;
               END IF;
 
            WHEN sendstatus_st =>
            -- Envoie du registre de status
               IF (datsent_free = '1') THEN
                  datsent <= reg_status;
                  valsent <= '1';
                  eof <= '1';
                  fsm_comexec <= endframe_st;
               END IF;
 
            WHEN endframe_st =>
            -- Gestion de la fin d'envoie d'une trame
               IF (datsent_free = '1') THEN
                  eof <= '0';
                  valsent <= '0';
                  fsm_comexec <= idle_st;
               END IF;
 
            WHEN purgecom_st =>
            -- Purge la mémoire de commande pour se recaler au début de la commande suivante
            -- en cas de problème ou en cas de commande déjà reçue
               store_com <= '0';
               delete_com <= '0';
               synchro_outwin <= '0';
               IF (sel_voie = '0') THEN
                  IF (com_dispo1 = '1' AND socin1 = '0') THEN
                  -- On lit jusqu'à ce qu'il n'y ait plus de commande ou qu'on ait atteind le début de la commande suivante
                     rd_com <= '1';
                  ELSE
                     rd_com <= '0';
                     fsm_comexec <= idle_st;
                  END IF;
               ELSE
                  IF (com_dispo2 = '1' AND socin2 = '0') THEN
                     rd_com <= '1';
                  ELSE
                     rd_com <= '0';
                     fsm_comexec <= idle_st;
                  END IF;
               END IF;
 
            WHEN OTHERS =>
               fsm_comexec <= idle_st;
         END CASE;
      END IF;
   END PROCESS;
 
end rtl;
 
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.