|
--------------------------------------------------------------------------------
|
|
--
|
|
-- FileName: spi_master.vhd
|
|
-- Dependencies: none
|
|
-- Design Software: Quartus II Version 9.0 Build 132 SJ Full Version
|
|
--
|
|
-- HDL CODE IS PROVIDED "AS IS." DIGI-KEY EXPRESSLY DISCLAIMS ANY
|
|
-- WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
-- PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY
|
|
-- BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL
|
|
-- DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF
|
|
-- PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
|
|
-- BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
|
|
-- ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS.
|
|
--
|
|
-- Version History
|
|
-- Version 1.0 7/23/2010 Scott Larson
|
|
-- Initial Public Release
|
|
-- Version 1.1 4/11/2013 Scott Larson
|
|
-- Corrected ModelSim simulation error (explicitly reset clk_toggles signal)
|
|
--
|
|
--------------------------------------------------------------------------------
|
|
|
|
LIBRARY ieee;
|
|
USE ieee.std_logic_1164.all;
|
|
USE ieee.std_logic_arith.all;
|
|
USE ieee.std_logic_unsigned.all;
|
|
|
|
ENTITY spi_master IS
|
|
GENERIC(
|
|
slaves : INTEGER := 4; --number of spi slaves
|
|
d_width : INTEGER := 2); --data bus width
|
|
PORT(
|
|
clock : IN STD_LOGIC; --system clock
|
|
reset_n : IN STD_LOGIC; --asynchronous reset
|
|
enable : IN STD_LOGIC; --initiate transaction
|
|
cpol : IN STD_LOGIC; --spi clock polarity
|
|
cpha : IN STD_LOGIC; --spi clock phase
|
|
cont : IN STD_LOGIC; --continuous mode command
|
|
clk_div : IN INTEGER; --system clock cycles per 1/2 period of sclk
|
|
addr : IN INTEGER; --address of slave
|
|
tx_data : IN STD_LOGIC_VECTOR(d_width-1 DOWNTO 0); --data to transmit
|
|
miso : IN STD_LOGIC; --master in, slave out
|
|
sclk : BUFFER STD_LOGIC; --spi clock
|
|
ss_n : BUFFER STD_LOGIC_VECTOR(slaves-1 DOWNTO 0); --slave select
|
|
mosi : OUT STD_LOGIC; --master out, slave in
|
|
busy : OUT STD_LOGIC; --busy / data ready signal
|
|
rx_data : OUT STD_LOGIC_VECTOR(d_width-1 DOWNTO 0)); --data received
|
|
END spi_master;
|
|
|
|
ARCHITECTURE logic OF spi_master IS
|
|
TYPE machine IS(ready, execute); --state machine data type
|
|
SIGNAL state : machine; --current state
|
|
SIGNAL slave : INTEGER; --slave selected for current transaction
|
|
SIGNAL clk_ratio : INTEGER; --current clk_div
|
|
SIGNAL count : INTEGER; --counter to trigger sclk from system clock
|
|
SIGNAL clk_toggles : INTEGER RANGE 0 TO d_width*2 + 1; --count spi clock toggles
|
|
SIGNAL assert_data : STD_LOGIC; --'1' is tx sclk toggle, '0' is rx sclk toggle
|
|
SIGNAL continue : STD_LOGIC; --flag to continue transaction
|
|
SIGNAL rx_buffer : STD_LOGIC_VECTOR(d_width-1 DOWNTO 0); --receive data buffer
|
|
SIGNAL tx_buffer : STD_LOGIC_VECTOR(d_width-1 DOWNTO 0); --transmit data buffer
|
|
SIGNAL last_bit_rx : INTEGER RANGE 0 TO d_width*2; --last rx data bit location
|
|
BEGIN
|
|
PROCESS(clock, reset_n)
|
|
BEGIN
|
|
|
|
IF(reset_n = '0') THEN --reset system
|
|
busy <= '1'; --set busy signal
|
|
ss_n <= (OTHERS => '1'); --deassert all slave select lines
|
|
mosi <= 'Z'; --set master out to high impedance
|
|
rx_data <= (OTHERS => '0'); --clear receive data port
|
|
state <= ready; --go to ready state when reset is exited
|
|
|
|
ELSIF(clock'EVENT AND clock = '1') THEN
|
|
CASE state IS --state machine
|
|
|
|
WHEN ready =>
|
|
busy <= '0'; --clock out not busy signal
|
|
ss_n <= (OTHERS => '1'); --set all slave select outputs high
|
|
mosi <= 'Z'; --set mosi output high impedance
|
|
continue <= '0'; --clear continue flag
|
|
|
|
--user input to initiate transaction
|
|
IF(enable = '1') THEN
|
|
busy <= '1'; --set busy signal
|
|
IF(addr < slaves) THEN --check for valid slave address
|
|
slave <= addr; --clock in current slave selection if valid
|
|
ELSE
|
|
slave <= 0; --set to first slave if not valid
|
|
END IF;
|
|
IF(clk_div = 0) THEN --check for valid spi speed
|
|
clk_ratio <= 1; --set to maximum speed if zero
|
|
count <= 1; --initiate system-to-spi clock counter
|
|
ELSE
|
|
clk_ratio <= clk_div; --set to input selection if valid
|
|
count <= clk_div; --initiate system-to-spi clock counter
|
|
END IF;
|
|
sclk <= cpol; --set spi clock polarity
|
|
assert_data <= NOT cpha; --set spi clock phase
|
|
tx_buffer <= tx_data; --clock in data for transmit into buffer
|
|
clk_toggles <= 0; --initiate clock toggle counter
|
|
last_bit_rx <= d_width*2 + conv_integer(cpha) - 1; --set last rx data bit
|
|
state <= execute; --proceed to execute state
|
|
ELSE
|
|
state <= ready; --remain in ready state
|
|
END IF;
|
|
|
|
WHEN execute =>
|
|
busy <= '1'; --set busy signal
|
|
ss_n(slave) <= '0'; --set proper slave select output
|
|
|
|
--system clock to sclk ratio is met
|
|
IF(count = clk_ratio) THEN
|
|
count <= 1; --reset system-to-spi clock counter
|
|
assert_data <= NOT assert_data; --switch transmit/receive indicator
|
|
IF(clk_toggles = d_width*2 + 1) THEN
|
|
clk_toggles <= 0; --reset spi clock toggles counter
|
|
ELSE
|
|
clk_toggles <= clk_toggles + 1; --increment spi clock toggles counter
|
|
END IF;
|
|
|
|
--spi clock toggle needed
|
|
IF(clk_toggles <= d_width*2 AND ss_n(slave) = '0') THEN
|
|
sclk <= NOT sclk; --toggle spi clock
|
|
END IF;
|
|
|
|
--receive spi clock toggle
|
|
IF(assert_data = '0' AND clk_toggles < last_bit_rx + 1 AND ss_n(slave) = '0') THEN
|
|
rx_buffer <= rx_buffer(d_width-2 DOWNTO 0) & miso; --shift in received bit
|
|
END IF;
|
|
|
|
--transmit spi clock toggle
|
|
IF(assert_data = '1' AND clk_toggles < last_bit_rx) THEN
|
|
mosi <= tx_buffer(d_width-1); --clock out data bit
|
|
tx_buffer <= tx_buffer(d_width-2 DOWNTO 0) & '0'; --shift data transmit buffer
|
|
END IF;
|
|
|
|
--last data receive, but continue
|
|
IF(clk_toggles = last_bit_rx AND cont = '1') THEN
|
|
tx_buffer <= tx_data; --reload transmit buffer
|
|
clk_toggles <= last_bit_rx - d_width*2 + 1; --reset spi clock toggle counter
|
|
continue <= '1'; --set continue flag
|
|
END IF;
|
|
|
|
--normal end of transaction, but continue
|
|
IF(continue = '1') THEN
|
|
continue <= '0'; --clear continue flag
|
|
busy <= '0'; --clock out signal that first receive data is ready
|
|
rx_data <= rx_buffer; --clock out received data to output port
|
|
END IF;
|
|
|
|
--end of transaction
|
|
IF((clk_toggles = d_width*2 + 1) AND cont = '0') THEN
|
|
busy <= '0'; --clock out not busy signal
|
|
ss_n <= (OTHERS => '1'); --set all slave selects high
|
|
mosi <= 'Z'; --set mosi output high impedance
|
|
rx_data <= rx_buffer; --clock out received data to output port
|
|
state <= ready; --return to ready state
|
|
ELSE --not end of transaction
|
|
state <= execute; --remain in execute state
|
|
END IF;
|
|
|
|
ELSE --system clock to sclk ratio not met
|
|
count <= count + 1; --increment counter
|
|
state <= execute; --remain in execute state
|
|
END IF;
|
|
|
|
END CASE;
|
|
END IF;
|
|
END PROCESS;
|
|
END logic;
|