1 http://eewiki.net/display/LOGIC/Serial+Peripheral+Interface+(SPI)+Master+(VHDL)
2
3 --------------------------------------------------------------------------------
4 --
5 -- FileName: spi_master.vhd
6 -- Dependencies: none
7 -- Design Software: Quartus II Version 9.0 Build 132 SJ Full Version
8 --
9 -- HDL CODE IS PROVIDED "AS IS." DIGI-KEY EXPRESSLY DISCLAIMS ANY
10 -- WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
11 -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
12 -- PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY
13 -- BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL
14 -- DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF
15 -- PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
16 -- BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
17 -- ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS.
18 --
19 -- Version History
20 -- Version 1.0 7/23/2010 Scott Larson
21 -- Initial Public Release
22 --
23 --------------------------------------------------------------------------------
24
25 library ieee;
26 use ieee.std_logic_1164.all;
27 use ieee.std_logic_arith.all;
28 use ieee.std_logic_unsigned.all;
29
30 entity spi_master is
31 generic(
32 slaves : INTEGER := 8; --number of spi slaves
33 d_width : INTEGER := 8); --data bus width
34 port(
35 clock : in STD_LOGIC; --system clock
36 reset_n : in STD_LOGIC; --asynchronous reset
37 enable : in STD_LOGIC; --initiate transaction
38 cpol : in STD_LOGIC; --spi clock polarity
39 cpha : in STD_LOGIC; --spi clock phase
40 cont : in STD_LOGIC; --continuous mode command
41 clk_div : in INTEGER; --system clock cycles per 1/2 period of sclk
42 addr : in INTEGER; --address of slave
43 tx_data : in STD_LOGIC_VECTOR(d_width-1 downto 0); --data to transmit
44 miso : in STD_LOGIC; --master in, slave out
45 sclk : buffer STD_LOGIC; --spi clock
46 ss_n : buffer STD_LOGIC_VECTOR(slaves-1 downto 0); --slave select
47 mosi : out STD_LOGIC; --master out, slave in
48 busy : out STD_LOGIC; --busy / data ready signal
49 rx_data : out STD_LOGIC_VECTOR(d_width-1 downto 0)); --data received
50 end spi_master;
51
52 architecture logic of spi_master is
53 type machine is(ready, execute); --state machine data type
54 signal state : machine; --current state
55 signal slave : INTEGER; --slave selected for current transaction
56 signal clk_ratio : INTEGER; --current clk_div
57 signal count : INTEGER; --counter to trigger sclk from system clock
58 signal clk_toggles : INTEGER range 0 to d_width*2 + 1; --count spi clock toggles
59 signal assert_data : STD_LOGIC; --'1' is tx sclk toggle, '0' is rx sclk toggle
60 signal continue : STD_LOGIC; --flag to continue transaction
61 signal rx_buffer : STD_LOGIC_VECTOR(d_width-1 downto 0); --receive data buffer
62 signal tx_buffer : STD_LOGIC_VECTOR(d_width-1 downto 0); --transmit data buffer
63 signal last_bit_rx : INTEGER range 0 to d_width*2; --last rx data bit location
64 begin
65 process(clock, reset_n)
66 begin
67
68 if(reset_n = '0') then --reset system
69 busy <= '1'; --set busy signal
70 ss_n <= (others => '1'); --deassert all slave select lines
71 mosi <= 'Z'; --set master out to high impedance
72 rx_data <= (others => '0'); --clear receive data port
73 state <= ready; --go to ready state when reset is exited
74
75 elsif(clock'EVENT and clock = '1') then
76 case state is --state machine
77
78 when ready =>
79 busy <= '0'; --clock out not busy signal
80 ss_n <= (others => '1'); --set all slave select outputs high
81 mosi <= 'Z'; --set mosi output high impedance
82 continue <= '0'; --clear continue flag
83
84 --user input to initiate transaction
85 if(enable = '1') then
86 busy <= '1'; --set busy signal
87 if(addr < slaves) then --check for valid slave address
88 slave <= addr; --clock in current slave selection if valid
89 else
90 slave <= 0; --set to first slave if not valid
91 end if;
92 if(clk_div = 0) then --check for valid spi speed
93 clk_ratio <= 1; --set to maximum speed if zero
94 count <= 1; --initiate system-to-spi clock counter
95 else
96 clk_ratio <= clk_div; --set to input selection if valid
97 count <= clk_div; --initiate system-to-spi clock counter
98 end if;
99 sclk <= cpol; --set spi clock polarity
100 assert_data <= not cpha; --set spi clock phase
101 tx_buffer <= tx_data; --clock in data for transmit into buffer
102 clk_toggles <= 0; --initiate clock toggle counter
103 last_bit_rx <= d_width*2 + conv_integer(cpha) - 1; --set last rx data bit
104 state <= execute; --proceed to execute state
105 else
106 state <= ready; --remain in ready state
107 end if;
108
109 when execute =>
110 busy <= '1'; --set busy signal
111 ss_n(slave) <= '0'; --set proper slave select output
112
113 --system clock to sclk ratio is met
114 if(count = clk_ratio) then
115 count <= 1; --reset system-to-spi clock counter
116 assert_data <= not assert_data; --switch transmit/receive indicator
117 clk_toggles <= clk_toggles + 1; --increment spi clock toggles counter
118
119 --spi clock toggle needed
120 if(clk_toggles <= d_width*2 and ss_n(slave) = '0') then
121 sclk <= not sclk; --toggle spi clock
122 end if;
123
124 --receive spi clock toggle
125 if(assert_data = '0' and clk_toggles < last_bit_rx + 1 and ss_n(slave) = '0') then
126 rx_buffer <= rx_buffer(d_width-2 downto 0) & miso; --shift in received bit
127 end if;
128
129 --transmit spi clock toggle
130 if(assert_data = '1' and clk_toggles < last_bit_rx) then
131 mosi <= tx_buffer(d_width-1); --clock out data bit
132 tx_buffer <= tx_buffer(d_width-2 downto 0) & '0'; --shift data transmit buffer
133 end if;
134
135 --last data receive, but continue
136 if(clk_toggles = last_bit_rx and cont = '1') then
137 tx_buffer <= tx_data; --reload transmit buffer
138 clk_toggles <= last_bit_rx - d_width*2 + 1; --reset spi clock toggle counter
139 continue <= '1'; --set continue flag
140 end if;
141
142 --normal end of transaction, but continue
143 if(continue = '1') then
144 continue <= '0'; --clear continue flag
145 busy <= '0'; --clock out signal that first receive data is ready
146 rx_data <= rx_buffer; --clock out received data to output port
147 end if;
148
149 --end of transaction
150 if((clk_toggles = d_width*2 + 1) and cont = '0') then
151 busy <= '0'; --clock out not busy signal
152 ss_n <= (others => '1'); --set all slave selects high
153 mosi <= 'Z'; --set mosi output high impedance
154 rx_data <= rx_buffer; --clock out received data to output port
155 state <= ready; --return to ready state
156 else --not end of transaction
157 state <= execute; --remain in execute state
158 end if;
159
160 else --system clock to sclk ratio not met
161 count <= count + 1; --increment counter
162 state <= execute; --remain in execute state
163 end if;
164
165 end case;
166 end if;
167 end process;
168 end logic;