1 |
2 |
dewhisna |
--------------------------------------------------------------------------------
|
2 |
|
|
--
|
3 |
|
|
-- FileName: spi_slave.vhd
|
4 |
|
|
-- Dependencies: none
|
5 |
|
|
-- Design Software: Quartus II 32-bit Version 11.1 Build 173 SJ Full Version
|
6 |
|
|
--
|
7 |
|
|
-- HDL CODE IS PROVIDED "AS IS." DIGI-KEY EXPRESSLY DISCLAIMS ANY
|
8 |
|
|
-- WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
9 |
|
|
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
10 |
|
|
-- PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY
|
11 |
|
|
-- BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL
|
12 |
|
|
-- DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF
|
13 |
|
|
-- PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
|
14 |
|
|
-- BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
|
15 |
|
|
-- ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS.
|
16 |
|
|
--
|
17 |
|
|
-- Version History
|
18 |
|
|
-- Version 1.0 7/5/2012 Scott Larson
|
19 |
|
|
-- Initial Public Release
|
20 |
|
|
-- Version 1.1 11/27/2012 Scott Larson
|
21 |
|
|
-- Added an asynchronous active low reset
|
22 |
|
|
--
|
23 |
|
|
--------------------------------------------------------------------------------
|
24 |
|
|
|
25 |
|
|
LIBRARY ieee;
|
26 |
|
|
USE ieee.std_logic_1164.all;
|
27 |
|
|
USE ieee.std_logic_arith.all;
|
28 |
|
|
|
29 |
|
|
ENTITY spi_slave IS
|
30 |
|
|
GENERIC(
|
31 |
|
|
cpol : STD_LOGIC := '0'; --spi clock polarity mode
|
32 |
|
|
cpha : STD_LOGIC := '0'; --spi clock phase mode
|
33 |
|
|
d_width : INTEGER := 8); --data width in bits
|
34 |
|
|
PORT(
|
35 |
|
|
sclk : IN STD_LOGIC; --spi clk from master
|
36 |
|
|
reset_n : IN STD_LOGIC; --active low reset
|
37 |
|
|
ss_n : IN STD_LOGIC; --active low slave select
|
38 |
|
|
mosi : IN STD_LOGIC; --master out, slave in
|
39 |
|
|
rx_req : IN STD_LOGIC; --'1' while busy = '0' moves data to the rx_data output
|
40 |
|
|
st_load_en : IN STD_LOGIC; --asynchronous load enable
|
41 |
|
|
st_load_trdy : IN STD_LOGIC; --asynchronous trdy load input
|
42 |
|
|
st_load_rrdy : IN STD_LOGIC; --asynchronous rrdy load input
|
43 |
|
|
st_load_roe : IN STD_LOGIC; --asynchronous roe load input
|
44 |
|
|
tx_load_en : IN STD_LOGIC; --asynchronous transmit buffer load enable
|
45 |
|
|
tx_load_data : IN STD_LOGIC_VECTOR(d_width-1 DOWNTO 0); --asynchronous tx data to load
|
46 |
|
|
trdy : BUFFER STD_LOGIC := '0'; --transmit ready bit
|
47 |
|
|
rrdy : BUFFER STD_LOGIC := '0'; --receive ready bit
|
48 |
|
|
roe : BUFFER STD_LOGIC := '0'; --receive overrun error bit
|
49 |
|
|
rx_data : OUT STD_LOGIC_VECTOR(d_width-1 DOWNTO 0) := (OTHERS => '0'); --receive register output to logic
|
50 |
|
|
busy : OUT STD_LOGIC := '0'; --busy signal to logic ('1' during transaction)
|
51 |
|
|
miso : OUT STD_LOGIC := 'Z'); --master in, slave out
|
52 |
|
|
END spi_slave;
|
53 |
|
|
|
54 |
|
|
ARCHITECTURE logic OF spi_slave IS
|
55 |
|
|
SIGNAL mode : STD_LOGIC; --groups modes by clock polarity relation to data
|
56 |
|
|
SIGNAL clk : STD_LOGIC; --clock
|
57 |
|
|
SIGNAL bit_cnt : STD_LOGIC_VECTOR(d_width+8 DOWNTO 0); --'1' for active transaction bit
|
58 |
|
|
SIGNAL wr_add : STD_LOGIC; --address of register to write ('0' = receive, '1' = status)
|
59 |
|
|
SIGNAL rd_add : STD_LOGIC; --address of register to read ('0' = transmit, '1' = status)
|
60 |
|
|
SIGNAL rx_buf : STD_LOGIC_VECTOR(d_width-1 DOWNTO 0) := (OTHERS => '0'); --receiver buffer
|
61 |
|
|
SIGNAL tx_buf : STD_LOGIC_VECTOR(d_width-1 DOWNTO 0) := (OTHERS => '0'); --transmit buffer
|
62 |
|
|
BEGIN
|
63 |
|
|
busy <= NOT ss_n; --high during transactions
|
64 |
|
|
|
65 |
|
|
--adjust clock so writes are on rising edge and reads on falling edge
|
66 |
|
|
mode <= cpol XOR cpha; --'1' for modes that write on rising edge
|
67 |
|
|
WITH mode SELECT
|
68 |
|
|
clk <= sclk WHEN '1',
|
69 |
|
|
NOT sclk WHEN OTHERS;
|
70 |
|
|
|
71 |
|
|
--keep track of miso/mosi bit counts for data alignmnet
|
72 |
|
|
PROCESS(ss_n, clk)
|
73 |
|
|
BEGIN
|
74 |
|
|
IF(ss_n = '1' OR reset_n = '0') THEN --this slave is not selected or being reset
|
75 |
|
|
bit_cnt <= (conv_integer(NOT cpha) => '1', OTHERS => '0'); --reset miso/mosi bit count
|
76 |
|
|
ELSE --this slave is selected
|
77 |
|
|
IF(rising_edge(clk)) THEN --new bit on miso/mosi
|
78 |
|
|
bit_cnt <= bit_cnt(d_width+8-1 DOWNTO 0) & '0'; --shift active bit indicator
|
79 |
|
|
END IF;
|
80 |
|
|
END IF;
|
81 |
|
|
END PROCESS;
|
82 |
|
|
|
83 |
|
|
PROCESS(ss_n, clk, st_load_en, tx_load_en, rx_req)
|
84 |
|
|
BEGIN
|
85 |
|
|
|
86 |
|
|
--write address register ('0' for receive, '1' for status)
|
87 |
|
|
IF(bit_cnt(1) = '1' AND falling_edge(clk)) THEN
|
88 |
|
|
wr_add <= mosi;
|
89 |
|
|
END IF;
|
90 |
|
|
|
91 |
|
|
--read address register ('0' for transmit, '1' for status)
|
92 |
|
|
IF(bit_cnt(2) = '1' AND falling_edge(clk)) THEN
|
93 |
|
|
rd_add <= mosi;
|
94 |
|
|
END IF;
|
95 |
|
|
|
96 |
|
|
--trdy register
|
97 |
|
|
IF((ss_n = '1' AND st_load_en = '1' AND st_load_trdy = '0') OR reset_n = '0') THEN
|
98 |
|
|
trdy <= '0'; --cleared by user logic or reset
|
99 |
|
|
ELSIF(ss_n = '1' AND ((st_load_en = '1' AND st_load_trdy = '1') OR tx_load_en = '1')) THEN
|
100 |
|
|
trdy <= '1'; --set when tx buffer written or set by user logic
|
101 |
|
|
ELSIF(wr_add = '1' AND bit_cnt(9) = '1' AND falling_edge(clk)) THEN
|
102 |
|
|
trdy <= mosi; --new value written over spi bus
|
103 |
|
|
ELSIF(rd_add = '0' AND bit_cnt(d_width+8) = '1' AND falling_edge(clk)) THEN
|
104 |
|
|
trdy <= '0'; --clear when transmit buffer read
|
105 |
|
|
END IF;
|
106 |
|
|
|
107 |
|
|
--rrdy register
|
108 |
|
|
IF((ss_n = '1' AND ((st_load_en = '1' AND st_load_rrdy = '0') OR rx_req = '1')) OR reset_n = '0') THEN
|
109 |
|
|
rrdy <= '0'; --cleared by user logic or rx_data has been requested or reset
|
110 |
|
|
ELSIF(ss_n = '1' AND st_load_en = '1' AND st_load_rrdy = '1') THEN
|
111 |
|
|
rrdy <= '1'; --set when set by user logic
|
112 |
|
|
ELSIF(wr_add = '1' AND bit_cnt(10) = '1' AND falling_edge(clk)) THEN
|
113 |
|
|
rrdy <= mosi; --new value written over spi bus
|
114 |
|
|
ELSIF(wr_add = '0' AND bit_cnt(d_width+8) = '1' AND falling_edge(clk)) THEN
|
115 |
|
|
rrdy <= '1'; --set when new data received
|
116 |
|
|
END IF;
|
117 |
|
|
|
118 |
|
|
--roe register
|
119 |
|
|
IF((ss_n = '1' AND st_load_en = '1' AND st_load_roe = '0') OR reset_n = '0') THEN
|
120 |
|
|
roe <= '0'; --cleared by user logic or reset
|
121 |
|
|
ELSIF(ss_n = '1' AND st_load_en = '1' AND st_load_roe = '1') THEN
|
122 |
|
|
roe <= '1'; --set by user logic
|
123 |
|
|
ELSIF(rrdy = '1' AND wr_add = '0' AND bit_cnt(d_width+8) = '1' AND falling_edge(clk)) THEN
|
124 |
|
|
roe <= '1'; --set by actual overrun
|
125 |
|
|
ELSIF(wr_add = '1' AND bit_cnt(11) = '1' AND falling_edge(clk)) THEN
|
126 |
|
|
roe <= mosi; --new value written by spi bus
|
127 |
|
|
END IF;
|
128 |
|
|
|
129 |
|
|
--receive registers
|
130 |
|
|
--write to the receive register from master
|
131 |
|
|
IF(reset_n = '0') THEN
|
132 |
|
|
rx_buf <= (OTHERS => '0');
|
133 |
|
|
ELSE
|
134 |
|
|
FOR i IN 0 TO d_width-1 LOOP
|
135 |
|
|
IF(wr_add = '0' AND bit_cnt(i+9) = '1' AND falling_edge(clk)) THEN
|
136 |
|
|
rx_buf(d_width-1-i) <= mosi;
|
137 |
|
|
END IF;
|
138 |
|
|
END LOOP;
|
139 |
|
|
END IF;
|
140 |
|
|
--fulfill user logic request for receive data
|
141 |
|
|
IF(reset_n = '0') THEN
|
142 |
|
|
rx_data <= (OTHERS => '0');
|
143 |
|
|
ELSIF(ss_n = '1' AND rx_req = '1') THEN
|
144 |
|
|
rx_data <= rx_buf;
|
145 |
|
|
END IF;
|
146 |
|
|
|
147 |
|
|
--transmit registers
|
148 |
|
|
IF(reset_n = '0') THEN
|
149 |
|
|
tx_buf <= (OTHERS => '0');
|
150 |
|
|
ELSIF(ss_n = '1' AND tx_load_en = '1') THEN --load transmit register from user logic
|
151 |
|
|
tx_buf <= tx_load_data;
|
152 |
|
|
ELSIF(rd_add = '0' AND bit_cnt(7 DOWNTO 0) = "00000000" AND bit_cnt(d_width+8) = '0' AND rising_edge(clk)) THEN
|
153 |
|
|
tx_buf(d_width-1 DOWNTO 0) <= tx_buf(d_width-2 DOWNTO 0) & tx_buf(d_width-1); --shift through tx data
|
154 |
|
|
END IF;
|
155 |
|
|
|
156 |
|
|
--miso output register
|
157 |
|
|
IF(ss_n = '1' OR reset_n = '0') THEN --no transaction occuring or reset
|
158 |
|
|
miso <= 'Z';
|
159 |
|
|
ELSIF(rd_add = '1' AND rising_edge(clk)) THEN --write status register to master
|
160 |
|
|
CASE bit_cnt(10 DOWNTO 8) IS
|
161 |
|
|
WHEN "001" => miso <= trdy;
|
162 |
|
|
WHEN "010" => miso <= rrdy;
|
163 |
|
|
WHEN "100" => miso <= roe;
|
164 |
|
|
WHEN OTHERS => NULL;
|
165 |
|
|
END CASE;
|
166 |
|
|
ELSIF(rd_add = '0' AND bit_cnt(7 DOWNTO 0) = "00000000" AND bit_cnt(d_width+8) = '0' AND rising_edge(clk)) THEN
|
167 |
|
|
miso <= tx_buf(d_width-1); --send transmit register data to master
|
168 |
|
|
END IF;
|
169 |
|
|
|
170 |
|
|
END PROCESS;
|
171 |
|
|
END logic;
|