1 |
2 |
dewhisna |
--------------------------------------------------------------------------------
|
2 |
|
|
-- File name : spi_slave.vhd
|
3 |
|
|
--------------------------------------------------------------------------------
|
4 |
|
|
-- Copyright (C) 2015 Donna Whisnant/Dewtronics.
|
5 |
|
|
-- Contact: http://www.dewtronics.com/
|
6 |
|
|
--
|
7 |
|
|
-- This file may be used under the terms of the GNU Lesser General Public License
|
8 |
|
|
-- version 3.0 as published by the Free Software Foundation and appearing
|
9 |
|
|
-- in the files lgpl-3.0.txt/gpl-3.0.txt included in the packaging of this file.
|
10 |
|
|
-- Please review the following information to ensure the GNU Lesser General
|
11 |
|
|
-- Public License version 3.0 requirements will be met:
|
12 |
|
|
-- https://www.gnu.org/licenses/lgpl-3.0.html
|
13 |
|
|
-- Attribution requested, but not required.
|
14 |
|
|
--
|
15 |
|
|
-- Target Device: Xilinx Spartan-6 XC6SLX9-2-TQG144
|
16 |
|
|
-- Using Numato Mimas Spartan 6 FPGA Development Board
|
17 |
|
|
-- http://numato.com/mimas-spartan-6-fpga-development-board.html
|
18 |
|
|
--
|
19 |
|
|
--
|
20 |
|
|
-- SPI Slave Entity for TimerOCD
|
21 |
|
|
--
|
22 |
|
|
--------------------------------------------------------------------------------
|
23 |
|
|
|
24 |
|
|
library IEEE;
|
25 |
|
|
use IEEE.std_logic_1164.all;
|
26 |
|
|
use IEEE.std_logic_arith.all;
|
27 |
|
|
use IEEE.std_logic_unsigned.all;
|
28 |
|
|
|
29 |
|
|
library UNISIM;
|
30 |
|
|
use UNISIM.VCOMPONENTS.all;
|
31 |
|
|
|
32 |
|
|
entity spi_slave is
|
33 |
|
|
generic (
|
34 |
|
|
cpol : STD_LOGIC := '0'; --spi clock polarity mode
|
35 |
|
|
cpha : STD_LOGIC := '0'; --spi clock phase mode
|
36 |
|
|
d_width : INTEGER := 8 --data width in bits
|
37 |
|
|
);
|
38 |
|
|
port (
|
39 |
|
|
sync_clk : in STD_LOGIC; --system clock in for synchronization of the SS signal
|
40 |
|
|
sclk : in STD_LOGIC; --spi clk from master
|
41 |
|
|
reset_n : in STD_LOGIC; --active low reset
|
42 |
|
|
ss_n : in STD_LOGIC; --active low slave select
|
43 |
|
|
mosi : in STD_LOGIC; --master out, slave in
|
44 |
|
|
rrdy : out STD_LOGIC := '0'; --receive ready bit
|
45 |
|
|
rx_data : out STD_LOGIC_VECTOR(d_width-1 downto 0) := (OTHERS => '0'); --receive register output to logic
|
46 |
|
|
busy : out STD_LOGIC := '0'; --busy signal to logic ('1' during transaction)
|
47 |
|
|
miso : out STD_LOGIC := 'Z'; --master in, slave out
|
48 |
|
|
tx_load_data : in STD_LOGIC_VECTOR(d_width-1 downto 0); --Tx data to load (synchronous with end of address receive)
|
49 |
|
|
addr : out STD_LOGIC_VECTOR(7 downto 0) := (OTHERS => '0'); -- Address to Read or Write (as received from SPI)
|
50 |
|
|
addr_latch : out STD_LOGIC := '0'; -- Address Latch Enable Output (Set to True when the address is ready on the output and data should be supplied)
|
51 |
|
|
cmd_load_data : in STD_LOGIC_VECTOR(3 downto 0) := (OTHERS => '0') -- Data nybble to shift out during low nybble of command byte receive
|
52 |
|
|
);
|
53 |
|
|
end spi_slave;
|
54 |
|
|
|
55 |
|
|
architecture logic of spi_slave is
|
56 |
|
|
signal mode : STD_LOGIC; --groups modes by clock polarity relation to data
|
57 |
|
|
signal clk : STD_LOGIC; --clock
|
58 |
|
|
constant nRSB : integer := 4; -- Number of RxState Bits (used for conversions)
|
59 |
|
|
signal rx_state : STD_LOGIC_VECTOR(nRSB-1 downto 0); -- Receive state-machine
|
60 |
|
|
signal rxbit_cnt : INTEGER range 0 to d_width-1; -- Bit count for data received
|
61 |
|
|
constant nTSB : integer := 3; -- Number of TxState Bits (used for conversions)
|
62 |
|
|
signal tx_state : STD_LOGIC_VECTOR(nTSB-1 downto 0); -- Transmit state-machine
|
63 |
|
|
constant nTBCSB : integer := 3; -- Transmit Bit Count State Bits (used for conversions)
|
64 |
|
|
signal txbit_flg : STD_LOGIC_VECTOR(nTBCSB-1 downto 0); -- Bit count for data sent
|
65 |
|
|
signal wr_add : STD_LOGIC := '1'; -- address of register to write ('0' = receive, '1' = status)
|
66 |
|
|
signal cmd_add : STD_LOGIC := '0'; -- command or data ('0' = data, '1' = command)
|
67 |
|
|
signal rx_buf : STD_LOGIC_VECTOR(d_width-2 downto 0) := (OTHERS => '0'); --receiver buffer
|
68 |
|
|
signal tx_buf : STD_LOGIC_VECTOR(d_width-1 downto 0) := (OTHERS => '0'); -- transmit buffer
|
69 |
|
|
signal cmd_buf : STD_LOGIC_VECTOR(2 downto 0) := (OTHERS => '0'); -- command buffer
|
70 |
|
|
signal out_data : STD_LOGIC; -- Output data gated to miso
|
71 |
|
|
signal syn_ss_n : STD_LOGIC; -- Synchronized SS_N signal
|
72 |
|
|
signal syn_mosi : STD_LOGIC; -- MOSI synchronized to sync_clk
|
73 |
|
|
signal syn_clk_fall : STD_LOGIC; -- CLK falling edge synchronized to sync_clk
|
74 |
|
|
signal syn_clk_rise : STD_LOGIC; -- CLK rising edge synchronized to sync_clk
|
75 |
|
|
signal resync_clk : STD_LOGIC_VECTOR(3 downto 0);
|
76 |
|
|
signal resync_ss : STD_LOGIC_VECTOR(1 downto 0);
|
77 |
|
|
signal resync_mosi : STD_LOGIC_VECTOR(1 downto 0);
|
78 |
|
|
signal rrdy_int : STD_LOGIC; -- Internal rrdy. Set in the Rx State-Machine. Used to trigger a delayed rrdy pulse back to the main logic to get data a setup/hold time
|
79 |
|
|
signal rrdy_out : STD_LOGIC; -- Output rrdy (used to eliminate buffer type)
|
80 |
|
|
signal addr_latch_int : STD_LOGIC; -- Internal addr_latch. Set in the Rx State-Machine. Used to trigger a delayed addr_latch pulse back to the main logic to get data a setup/hold time
|
81 |
|
|
signal addr_latch_out : STD_LOGIC; -- Output addr_latch (used to eliminate buffer type)
|
82 |
|
|
begin
|
83 |
|
|
--adjust clock so writes are on rising edge and reads on falling edge
|
84 |
|
|
mode <= cpol XOR cpha; --'1' for modes that write on rising edge
|
85 |
|
|
with mode select
|
86 |
|
|
clk <= sclk when '1', NOT sclk when OTHERS;
|
87 |
|
|
|
88 |
|
|
-- Input synchronization:
|
89 |
|
|
sync_clk_proc:process(sync_clk)
|
90 |
|
|
begin
|
91 |
|
|
if (rising_edge(sync_clk)) then
|
92 |
|
|
syn_ss_n <= resync_ss(0);
|
93 |
|
|
syn_mosi <= resync_mosi(0);
|
94 |
|
|
resync_clk <= resync_clk(2 downto 0) & clk;
|
95 |
|
|
resync_ss <= resync_ss(0) & ss_n;
|
96 |
|
|
resync_mosi <= resync_mosi(0) & mosi;
|
97 |
|
|
end if;
|
98 |
|
|
end process sync_clk_proc;
|
99 |
|
|
syn_clk_fall <= resync_clk(3) and not resync_clk(2); -- '1' when going from 1 -> 0
|
100 |
|
|
syn_clk_rise <= not resync_clk(2) and resync_clk(1); -- '1' when going from 0 -> 1
|
101 |
|
|
|
102 |
|
|
-- output drive:
|
103 |
|
|
with (syn_ss_n OR (NOT reset_n)) select
|
104 |
|
|
miso <= 'Z' when '1', out_data when OTHERS;
|
105 |
|
|
|
106 |
|
|
busy <= NOT syn_ss_n; --high during transactions
|
107 |
|
|
|
108 |
|
|
-- rrdy synchronization:
|
109 |
|
|
-- Since rxspiproc is triggered on the rising_edge of
|
110 |
|
|
-- sync_clk, this process delays reporting it
|
111 |
|
|
-- by 1/2 clock cycle, giving the data time to
|
112 |
|
|
-- get stored:
|
113 |
|
|
rrdyproc:process(syn_ss_n, sync_clk, reset_n)
|
114 |
|
|
begin
|
115 |
|
|
if ((syn_ss_n = '1') OR (reset_n = '0')) then
|
116 |
|
|
rrdy_out <= '0';
|
117 |
|
|
elsif (falling_edge(sync_clk)) then
|
118 |
|
|
if (rrdy_out = '0') then
|
119 |
|
|
rrdy_out <= rrdy_int;
|
120 |
|
|
end if;
|
121 |
|
|
end if;
|
122 |
|
|
end process rrdyproc;
|
123 |
|
|
rrdy <= rrdy_out;
|
124 |
|
|
|
125 |
|
|
-- addr_latch synchronization:
|
126 |
|
|
-- Since rxspiproc is triggered on the rising_edge of
|
127 |
|
|
-- sync clk, this process delays reporting it
|
128 |
|
|
-- by 1/2 clock cycle, giving the data time to
|
129 |
|
|
-- get stored:
|
130 |
|
|
addrlatchproc:process(syn_ss_n, sync_clk, reset_n)
|
131 |
|
|
begin
|
132 |
|
|
if ((syn_ss_n = '1') OR (reset_n = '0')) then
|
133 |
|
|
addr_latch_out <= '0';
|
134 |
|
|
elsif (falling_edge(sync_clk)) then
|
135 |
|
|
if (addr_latch_out = '0') then
|
136 |
|
|
addr_latch_out <= addr_latch_int;
|
137 |
|
|
end if;
|
138 |
|
|
end if;
|
139 |
|
|
end process addrlatchproc;
|
140 |
|
|
addr_latch <= addr_latch_out;
|
141 |
|
|
|
142 |
|
|
-- rxspiproc: Receives incoming data
|
143 |
|
|
rxspiproc:process(syn_ss_n, syn_clk_fall, reset_n)
|
144 |
|
|
begin
|
145 |
|
|
if ((syn_ss_n = '1') OR (reset_n = '0')) then
|
146 |
|
|
rx_state <= (OTHERS => '0');
|
147 |
|
|
rx_buf <= (OTHERS => '0');
|
148 |
|
|
rxbit_cnt <= d_width-1;
|
149 |
|
|
rrdy_int <= '0';
|
150 |
|
|
addr_latch_int <= '0';
|
151 |
|
|
elsif (falling_edge(syn_clk_fall)) then
|
152 |
|
|
case CONV_INTEGER(rx_state) is
|
153 |
|
|
when 0 =>
|
154 |
|
|
--read/write mode ('0' for write, '1' for read)
|
155 |
|
|
wr_add <= syn_mosi;
|
156 |
|
|
addr(7) <= syn_mosi;
|
157 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(1, nRSB);
|
158 |
|
|
when 1 =>
|
159 |
|
|
--cmd/data mode ('0' for data, '1' for cmd)
|
160 |
|
|
cmd_add <= syn_mosi;
|
161 |
|
|
addr(6) <= syn_mosi;
|
162 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(3, nRSB);
|
163 |
|
|
when 3 =>
|
164 |
|
|
addr(5) <= syn_mosi;
|
165 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(2, nRSB);
|
166 |
|
|
when 2 =>
|
167 |
|
|
addr(4) <= syn_mosi;
|
168 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(6, nRSB);
|
169 |
|
|
when 6 =>
|
170 |
|
|
addr(3) <= syn_mosi;
|
171 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(7, nRSB);
|
172 |
|
|
when 7 =>
|
173 |
|
|
addr(2) <= syn_mosi;
|
174 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(5, nRSB);
|
175 |
|
|
when 5 =>
|
176 |
|
|
addr(1) <= syn_mosi;
|
177 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(4, nRSB);
|
178 |
|
|
when 4 =>
|
179 |
|
|
addr(0) <= syn_mosi;
|
180 |
|
|
if (cmd_add = '1') then
|
181 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(13, nRSB);
|
182 |
|
|
if (wr_add = '0') then
|
183 |
|
|
rrdy_int <= '1';
|
184 |
|
|
end if;
|
185 |
|
|
else
|
186 |
|
|
addr_latch_int <= '1';
|
187 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(12, nRSB);
|
188 |
|
|
end if;
|
189 |
|
|
when 12 =>
|
190 |
|
|
if (rxbit_cnt = 0) then
|
191 |
|
|
if (wr_add = '0') then
|
192 |
|
|
rx_data <= rx_buf & syn_mosi;
|
193 |
|
|
rrdy_int <= '1';
|
194 |
|
|
end if;
|
195 |
|
|
rx_state <= CONV_STD_LOGIC_VECTOR(13, nRSB);
|
196 |
|
|
else
|
197 |
|
|
if (wr_add = '0') then
|
198 |
|
|
rx_buf <= rx_buf(rx_buf'HIGH-1 downto 0) & syn_mosi;
|
199 |
|
|
end if;
|
200 |
|
|
rxbit_cnt <= (rxbit_cnt - 1);
|
201 |
|
|
-- Stay in this state until all bits are received
|
202 |
|
|
end if;
|
203 |
|
|
when 13 =>
|
204 |
|
|
-- Stay in this state. The SS_n signal will release us
|
205 |
|
|
when OTHERS =>
|
206 |
|
|
end case;
|
207 |
|
|
end if;
|
208 |
|
|
end process rxspiproc;
|
209 |
|
|
|
210 |
|
|
|
211 |
|
|
-- txspiproc: Transmits outgoing data
|
212 |
|
|
-- Note: Commented out code for cmd_buf/cmd_load_data and
|
213 |
|
|
-- tx_buf/tx_load_data can be used to determine the point
|
214 |
|
|
-- where data must be ready/available from the main TimerOCD
|
215 |
|
|
-- relative to the SPI traffic. This allows for faster SPI
|
216 |
|
|
-- clock rates by only requiring the first couple of bits and/or
|
217 |
|
|
-- first byte of data to be valid when the SPI clock begins
|
218 |
|
|
-- shifting it out. Since several memory read operations are
|
219 |
|
|
-- required to obtain the data needed to be sent, this allows
|
220 |
|
|
-- for several SPI clock cycle times before that data reading
|
221 |
|
|
-- must be completed.
|
222 |
|
|
txspiproc:process(syn_ss_n, syn_clk_rise, reset_n)
|
223 |
|
|
begin
|
224 |
|
|
if ((syn_ss_n = '1') OR (reset_n = '0')) then
|
225 |
|
|
tx_state <= (OTHERS => '0');
|
226 |
|
|
tx_buf <= (OTHERS => '0');
|
227 |
|
|
txbit_flg <= (OTHERS => '0');
|
228 |
|
|
out_data <= '0';
|
229 |
|
|
elsif (rising_edge(syn_clk_rise)) then
|
230 |
|
|
case CONV_INTEGER(tx_state) is
|
231 |
|
|
when 0 =>
|
232 |
|
|
tx_state <= CONV_STD_LOGIC_VECTOR(1, nTSB);
|
233 |
|
|
when 1 =>
|
234 |
|
|
tx_state <= CONV_STD_LOGIC_VECTOR(3, nTSB);
|
235 |
|
|
when 3 =>
|
236 |
|
|
tx_state <= CONV_STD_LOGIC_VECTOR(2, nTSB);
|
237 |
|
|
when 2 =>
|
238 |
|
|
out_data <= cmd_load_data(3);
|
239 |
|
|
-- cmd_buf <= cmd_load_data(2 downto 0);
|
240 |
|
|
tx_state <= CONV_STD_LOGIC_VECTOR(6, nTSB);
|
241 |
|
|
when 6 =>
|
242 |
|
|
-- out_data <= cmd_buf(2);
|
243 |
|
|
out_data <= cmd_load_data(2);
|
244 |
|
|
cmd_buf <= cmd_load_data(2 downto 0);
|
245 |
|
|
tx_state <= CONV_STD_LOGIC_VECTOR(7, nTSB);
|
246 |
|
|
when 7 =>
|
247 |
|
|
out_data <= cmd_buf(1);
|
248 |
|
|
tx_state <= CONV_STD_LOGIC_VECTOR(5, nTSB);
|
249 |
|
|
when 5 =>
|
250 |
|
|
out_data <= cmd_buf(0);
|
251 |
|
|
tx_state <= CONV_STD_LOGIC_VECTOR(4, nTSB);
|
252 |
|
|
when 4 =>
|
253 |
|
|
if (cmd_add='0') then
|
254 |
|
|
case CONV_INTEGER(txbit_flg) is
|
255 |
|
|
when 0 =>
|
256 |
|
|
out_data <= tx_load_data(d_width-1);
|
257 |
|
|
-- tx_buf <= tx_load_data(d_width-2 downto 0) & "0";
|
258 |
|
|
txbit_flg <= CONV_STD_LOGIC_VECTOR(1, nTBCSB);
|
259 |
|
|
when 1 =>
|
260 |
|
|
out_data <= tx_load_data(d_width-2);
|
261 |
|
|
-- tx_buf <= tx_load_data(d_width-3 downto 0) & "00";
|
262 |
|
|
txbit_flg <= CONV_STD_LOGIC_VECTOR(3, nTBCSB);
|
263 |
|
|
when 3 =>
|
264 |
|
|
out_data <= tx_load_data(d_width-3);
|
265 |
|
|
-- tx_buf <= tx_load_data(d_width-4 downto 0) & "000";
|
266 |
|
|
txbit_flg <= CONV_STD_LOGIC_VECTOR(2, nTBCSB);
|
267 |
|
|
when 2 =>
|
268 |
|
|
out_data <= tx_load_data(d_width-4);
|
269 |
|
|
-- tx_buf <= tx_load_data(d_width-5 downto 0) & "0000";
|
270 |
|
|
txbit_flg <= CONV_STD_LOGIC_VECTOR(6, nTBCSB);
|
271 |
|
|
when 6 =>
|
272 |
|
|
out_data <= tx_load_data(d_width-5);
|
273 |
|
|
-- tx_buf <= tx_load_data(d_width-6 downto 0) & "00000";
|
274 |
|
|
txbit_flg <= CONV_STD_LOGIC_VECTOR(7, nTBCSB);
|
275 |
|
|
when 7 =>
|
276 |
|
|
out_data <= tx_load_data(d_width-6);
|
277 |
|
|
-- tx_buf <= tx_load_data(d_width-7 downto 0) & "000000";
|
278 |
|
|
txbit_flg <= CONV_STD_LOGIC_VECTOR(5, nTBCSB);
|
279 |
|
|
when 5 =>
|
280 |
|
|
out_data <= tx_load_data(d_width-7);
|
281 |
|
|
tx_buf <= tx_load_data(d_width-8 downto 0) & "0000000";
|
282 |
|
|
txbit_flg <= CONV_STD_LOGIC_VECTOR(4, nTBCSB);
|
283 |
|
|
when 4 =>
|
284 |
|
|
out_data <= tx_buf(tx_buf'HIGH);
|
285 |
|
|
tx_buf <= tx_buf(tx_buf'HIGH-1 downto 0) & "0";
|
286 |
|
|
-- Stay here and shift out remaining data. SS_n will release us
|
287 |
|
|
when OTHERS =>
|
288 |
|
|
end case;
|
289 |
|
|
else
|
290 |
|
|
out_data <= '0';
|
291 |
|
|
end if;
|
292 |
|
|
-- stay in this state shifting data out
|
293 |
|
|
when OTHERS => -- There are no other states, but keep ghdl happy
|
294 |
|
|
end case;
|
295 |
|
|
end if;
|
296 |
|
|
end process txspiproc;
|
297 |
|
|
end logic;
|