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