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 |
|
|
|