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;