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

Subversion Repositories saturn

[/] [saturn/] [trunk/] [IPCommunication/] [if_promspi.vhd] - Blame information for rev 2

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 DavidRAMBA
--============================================================================= 
2
--  TITRE : IF_PROMSPI
3
--  DESCRIPTION : 
4
--        Implémente une interface SPI master avec une vitesse de clk_sys / div_rate 
5
--        Le bus parallèle accepte en entrée un flux de données à écrire et fourni 
6
--        en sortie un flux de données lues.
7
--        Il supporte 2 types de commandes selon type_com et sur ordre de exec_com
8
--          type_com    |           opération
9
--             0        | Emet sur SPI la totalité des octets fournis précédemment sur TX_DAT
10
--             1        | Emet sur SPI la totalité des octets fournis précédemment sur TX_DAT
11
--                      | et récupère en lecture sur SPI nb_read octet et les met à disposition
12
--                      | sur RX_DAT
13
--  FICHIER :        if_promspi.vhd 
14
--=============================================================================
15
--  CREATION 
16
--  DATE              AUTEUR    PROJET  REVISION 
17
--  10/04/2014  DRA        SATURN       V1.0 
18
--=============================================================================
19
--  HISTORIQUE  DES  MODIFICATIONS :
20
--  DATE              AUTEUR    PROJET  REVISION 
21
--=============================================================================
22
 
23
LIBRARY IEEE;
24
USE IEEE.STD_LOGIC_1164.ALL;
25
USE IEEE.STD_LOGIC_ARITH.ALL;
26
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
27
 
28
ENTITY if_promspi IS
29
   GENERIC (
30
      div_rate    : INTEGER := 1;      -- Diviseur d'horloge système pour obtenir le débit SPI = 2^div_rate
31
      spiclk_freq : INTEGER := 10      -- Fréquence horloge SPI (MHz) = freq(clk_sys)/2^div_rate
32
      );
33
   PORT (
34
      -- Ports système
35
      clk_sys     : IN  STD_LOGIC;  -- Clock système à 20 MHz
36
      rst_n       : IN  STD_LOGIC;  -- Reset général système
37
 
38
      -- Interface série
39
      spi_csn     : OUT STD_LOGIC;  -- CS de la mémoire SPI
40
      spi_wpn     : OUT STD_LOGIC;  -- Write Protect de la mémoire SPI
41
      spi_sdo     : OUT STD_LOGIC;  -- Data Write
42
      spi_sdi     : IN  STD_LOGIC;  -- Data Read
43
      spi_clk     : OUT STD_LOGIC;  -- SPI Clock en (CPOL, CPHA) = (0, 0)
44
 
45
      -- Interface parallèle
46
      tx_dat      : IN  STD_LOGIC_VECTOR(7 downto 0);  -- Commande + Données à transmettre en SPI
47
      tx_val      : IN  STD_LOGIC;                     -- Validant du bus tx_dat
48
      rx_dat      : OUT STD_LOGIC_VECTOR(7 downto 0);  -- Données lues sur le bus SPI
49
      rx_val      : OUT STD_LOGIC;                     -- Signal indiquant la validité de la donnée sur le bus rx
50
      rx_next     : IN  STD_LOGIC;                     -- Indique qu'il ya des donénes dans la FIFO de réception
51
      type_com    : IN  STD_LOGIC;                     -- Code du type de commande à éxécuter
52
      exec_com    : IN  STD_LOGIC;                     -- Ordre d'exécution de la commande codée par type_com
53
      spi_busy    : OUT STD_LOGIC;                     -- Signale que l'interface SPI est occupée avec une commande
54
      nb_read     : IN  STD_LOGIC_VECTOR(7 DOWNTO 0)   -- Nombre d'octets à lire pour les commandes de type READ
55
      );
56
END if_promspi;
57
 
58
ARCHITECTURE rtl of if_promspi is
59
   CONSTANT com_wronly  : STD_LOGIC := '0';                       -- Commande de type Write Only
60
   CONSTANT com_wrrd    : STD_LOGIC := '1';                       -- Commande de type Write + Read (nb_read)
61
   SIGNAL rst           : STD_LOGIC;                              -- Reset des FIFO Tx et Rx
62
   SIGNAL div_clk       : STD_LOGIC_VECTOR(div_rate-1 DOWNTO 0);  -- Compteur pour diviser l'horloge système
63
   SIGNAL rise_clk      : STD_LOGIC;                              -- Front montant de l'horloge SPI
64
   SIGNAL fall_clk      : STD_LOGIC;                              -- Front descendant de l'horloge SPI
65
   SIGNAL fallclk_r     : STD_LOGIC;                              -- Front descendant de l'horloge SPI retardé d'un clk
66
   SIGNAL mask_clk      : STD_LOGIC;                              -- Signal de masque de l'horloge SPI
67
   SIGNAL cpt_bit       : STD_LOGIC_VECTOR(2 DOWNTO 0);           -- Compteur de bit pour le ser/deser
68
   SIGNAL cpt_byt       : STD_LOGIC_VECTOR(7 DOWNTO 0);           -- Compteur d'octets pour la réception
69
   SIGNAL fifotx_rd     : STD_LOGIC;                              -- Signal de lecture de la FIFO Tx
70
   SIGNAL data_tx       : STD_LOGIC_VECTOR(7 DOWNTO 0);           -- Data lue dans la FIFO Tx
71
   SIGNAL fifotx_empty  : STD_LOGIC;                              -- FIFO Tx vide
72
   SIGNAL fiforx_wr     : STD_LOGIC;                              -- Signal d'écriture dans la FIFO Rx
73
   SIGNAL fiforx_empty  : STD_LOGIC;                              -- FIFO Rx vide
74
   SIGNAL shifter_tx    : STD_LOGIC_VECTOR(7 DOWNTO 0);           -- Serialisateur
75
   SIGNAL shifter_rx    : STD_LOGIC_VECTOR(7 DOWNTO 0);           -- Déserialisateur
76
   SIGNAL read_stat     : STD_LOGIC;                              -- A 1 lors d'une lecture du registre de status
77
   SIGNAL latch_typcom  : STD_LOGIC;                              -- Pour mémoriser le type de commande
78
 
79
   -- Machine d'état de gestion des commandes
80
   TYPE fsmspi_typ IS (idle_st, latchcom_st, sendcom_st, getdat_st, releasecs_st, readstat_st, endcom_st);
81
   SIGNAL fsm_spi    : fsmspi_typ;
82
 
83
   -- FIFO (256 x 8bits) TX et RX en FWFT
84
   COMPONENT fifo_spi
85
   PORT (
86
      clk   : IN STD_LOGIC;
87
      rst   : IN STD_LOGIC;
88
      din   : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
89
      wr_en : IN STD_LOGIC;
90
      rd_en : IN STD_LOGIC;
91
      dout  : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
92
      full  : OUT STD_LOGIC;
93
      empty : OUT STD_LOGIC
94
      );
95
   END COMPONENT;
96
 
97
BEGIN
98
   rst <= NOT(rst_n);
99
 
100
   ------------------------------------------
101
   -- Process de generation de la clock SPI
102
   ------------------------------------------
103
   gen_clk : PROCESS(clk_sys, rst_n)
104
   BEGIN
105
      IF (rst_n = '0') THEN
106
         div_clk <= (OTHERS => '0');
107
         fallclk_r <= '0';
108
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
109
         div_clk <= div_clk + 1;
110
         fallclk_r <= fall_clk;
111
      END IF;
112
   END PROCESS;
113
   spi_clk <= mask_clk AND div_clk(div_clk'LEFT);     -- La clock est active en fonction de la machine d'état
114
   -- rise_clk est actif au cycle qui précède le front montant effectif
115
   rise_clk <= '1' WHEN (div_clk = CONV_STD_LOGIC_VECTOR(2**(div_rate-1)-1, div_rate)) ELSE '0';
116
   -- fall_clk est actif au cycle qui précède le front descendant effectif
117
   fall_clk <= '1' WHEN (div_clk = CONV_STD_LOGIC_VECTOR(2**div_rate-1, div_rate)) ELSE '0';
118
 
119
   -------------------------------------------------
120
   -- Machine d'état d'exécution des commandes SPI
121
   -------------------------------------------------
122
   spi_sdo <= shifter_tx(7);           -- On SER les data MSB first
123
   spi_wpn <= '1';                     -- L'écriture est toujours autorisée dans la flash
124
   man_fm : PROCESS(clk_sys, rst_n)
125
   BEGIN
126
      IF (rst_n = '0') THEN
127
         fsm_spi <= idle_st;
128
         spi_csn <= '1';
129
         mask_clk <= '0';
130
         spi_busy <= '1';              -- En reset, on indique le SPI est inutilisable
131
         shifter_tx <= (OTHERS => '0');
132
         fifotx_rd <= '0';
133
         shifter_rx <= (OTHERS => '0');
134
         fiforx_wr <= '0';
135
         read_stat <= '0';
136
         latch_typcom <= '0';
137
         cpt_bit <= "000";
138
         cpt_byt <= (OTHERS => '0');
139
      ELSIF (clk_sys'EVENT and clk_sys = '1') THEN
140
         CASE fsm_spi IS
141
            WHEN idle_st =>
142
            -- Etat d'attente d'une nouvelle commande à envoyer
143
               IF (exec_com = '1' AND fifotx_empty = '0') THEN
144
               -- Si on reçoit une nouvelle commande et si il y'a effectivement des octets à transmettre
145
                  spi_busy <= '1';           -- On signale qu'on est occupé
146
                  latch_typcom <= type_com;  -- On mémorise le type de la commande
147
                  fsm_spi <= latchcom_st;
148
               ELSE
149
                  spi_busy <= '0';
150
               END IF;
151
 
152
            WHEN latchcom_st =>
153
            -- Etat d'attent du 1er front montant de sclk
154
               IF (rise_clk = '1') THEN
155
                  spi_csn <= '0';         -- On active la Flash
156
                  cpt_bit <= "000";       -- On initialise le compteur de bit de façon à traiter le 1er octet (cf état suivant)
157
                  read_stat <= '0';       -- Par défaut, on ne lit pas encore le registre de status
158
                  fsm_spi <= sendcom_st;  -- On va envoyer la commande
159
               END IF;
160
 
161
            WHEN sendcom_st =>
162
            -- Etat de transmission de la commande
163
               IF (fall_clk = '1') THEN
164
               -- Sur chaque front descendant de la clock SPI
165
                  IF (cpt_bit = "000") THEN
166
                  -- Si on a transmis tous les bits (ou bien aucun pour le premier passage)
167
                     cpt_bit <= "111";
168
                     IF (fifotx_empty = '0' AND read_stat = '0') THEN
169
                     -- S'il reste des octets à transmettre et qu'on est pas en train de lire de status
170
                     -- note : si read_stat = 1 et fifo Tx n'est pas vide est un cas d'erreur
171
                        shifter_tx <= data_tx;  -- On va le SER
172
                        fifotx_rd <= '1';       -- On lit l'octet suivant dans la FIFO
173
                     ELSE
174
                     -- Si on a fini de transmettre les octets
175
                        IF (latch_typcom = com_wrrd or read_stat = '1') THEN
176
                        -- Si c'est une commande de type WR+RD ou bien si on lit le registre de staut
177
                           cpt_byt <= nb_read;     -- On va lire nb_read octets (ou une infinité si read_stat)
178
                           fsm_spi <= getdat_st;
179
                        ELSE
180
                        -- Si c'est une commande WR only
181
                           mask_clk <= '0';           -- On masque l'horloge
182
                           fsm_spi <= releasecs_st;   -- on va préparer la lecture du status
183
                        END IF;
184
                     END IF;
185
                  ELSE
186
                  -- Si on a pas SER tous les bits de l'octets
187
                     shifter_tx <= shifter_tx(6 DOWNTO 0) & '0';
188
                     cpt_bit <= cpt_bit - 1;
189
                     fifotx_rd <= '0';
190
                  END IF;
191
               ELSE
192
               -- On s'assure que le read dans la FIFO Tx ne dure qu'un cycle
193
                  fifotx_rd <= '0';
194
                  IF (fallclk_r = '1') THEN
195
                  -- Ici, on s'assure que le masque de l'horloge est enclenché un cycle d'horloge après le front descendant effectif
196
                  -- de la clock spi (évite les glitches)
197
                     mask_clk <= '1';
198
                  END IF;
199
               END IF;
200
 
201
            WHEN getdat_st =>
202
            -- Etat de réception des données de la FIFO
203
            -- Quand on arrive ici la première fois, nb_bit vaut déjà "111"
204
               IF (rise_clk = '1') THEN
205
               -- On échantillone sur front montant de la clock SPI
206
                  shifter_rx <= shifter_rx(6 DOWNTO 0) & spi_sdi; -- On DESER MSB first
207
               END IF;
208
               IF (fall_clk = '1') THEN
209
                  IF (cpt_bit = "000") THEN
210
                  -- Si on a DESER les 8 bits
211
                     fiforx_wr <= NOT(read_stat);          -- On stocke la donnée dans la FIFO Rx si c'est pas une lecture de status
212
                     IF ((latch_typcom = com_wrrd AND cpt_byt = CONV_STD_LOGIC_VECTOR(1, cpt_byt'LENGTH)) OR
213
                         (read_stat = '1' AND spi_sdi = '0')) THEN
214
                     -- Si c'est une commande WR+RD(nb_read) et qu'on a reçu les nb_read octets ou bien
215
                     -- on était en train de lire le registre de status et le LSB (WIP) du registre de status est nul
216
                        mask_clk <= '0';         -- On a fini
217
                        read_stat <= '0';
218
                        fsm_spi <= endcom_st;
219
                     ELSE
220
                     -- Si on doit encore récupérer des octets
221
                        cpt_byt <= cpt_byt - 1;
222
                     END IF;
223
                     cpt_bit <= "111";
224
                  ELSE
225
                  -- Si on n'a pas encore 7 bits
226
                     cpt_bit <= cpt_bit - 1;
227
                     fiforx_wr <= '0';
228
                  END IF;
229
               ELSE
230
               -- On s'assure que le signal de WR dans la FIFO Rx ne dure que 1 cycle
231
                  fiforx_wr <= '0';
232
               END IF;
233
 
234
            WHEN releasecs_st =>
235
            -- Etat de relachement du CS avant envoie de la commande de lecture du status.
236
               IF (rise_clk = '1') THEN
237
                  spi_csn <= '1';
238
                  -- On ajuste la tempo pour assurer que le release du CS fait bien 100ns quel que soit la clock SPI
239
                  cpt_byt <= CONV_STD_LOGIC_VECTOR(100*spiclk_freq/1000+1, cpt_byt'LENGTH);
240
                  fsm_spi <= readstat_st;
241
               END IF;
242
 
243
            WHEN readstat_st =>
244
            -- Etat de temporisation avant d'envoyer la commande de lecture du status 
245
               IF (rise_clk = '1') THEN
246
                  cpt_byt <= cpt_byt - 1;
247
               END IF;
248
               IF (fall_clk = '1' AND cpt_byt = CONV_STD_LOGIC_VECTOR(0, cpt_byt'LENGTH)) THEN
249
                  spi_csn <= '0';            -- On recommence un cycle SPI
250
                  cpt_bit <= "111";
251
                  shifter_tx <= x"05";       -- On force la transmission de la commande de lecture du status
252
                  read_stat <= '1';
253
                  fsm_spi <= sendcom_st;
254
               END IF;
255
 
256
            WHEN endcom_st =>
257
            -- Etat de fin, on attend un front montant de la clock SPI pour désactiver le CS.
258
               fiforx_wr <= '0';
259
               IF (rise_clk = '1') THEN
260
                  spi_busy <= '0';
261
                  spi_csn <= '1';
262
                  fsm_spi <= idle_st;
263
               END IF;
264
 
265
            WHEN OTHERS =>
266
               fsm_spi <= idle_st;
267
         END CASE;
268
      END IF;
269
   END PROCESS;
270
 
271
   inst_fiftx : fifo_spi
272
   PORT MAP (
273
      clk =>   clk_sys,
274
      rst =>   rst,
275
      din =>   tx_dat,
276
      wr_en => tx_val,
277
      rd_en => fifotx_rd,
278
      dout =>  data_tx,
279
      full =>  OPEN,
280
      empty => fifotx_empty
281
   );
282
 
283
   inst_fifrx : fifo_spi
284
   PORT MAP (
285
      clk =>   clk_sys,
286
      rst =>   rst,
287
      din =>   shifter_rx,
288
      wr_en => fiforx_wr,
289
      rd_en => rx_next,
290
      dout =>  rx_dat,
291
      full =>  OPEN,
292
      empty => fiforx_empty
293
   );
294
   rx_val <= NOT(fiforx_empty);
295
 
296
END rtl;
297
 

powered by: WebSVN 2.1.0

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