OpenCores
URL https://opencores.org/ocsvn/timerocd/timerocd/trunk

Subversion Repositories timerocd

[/] [timerocd/] [trunk/] [src/] [spi_slave.vhd] - Rev 2

Compare with Previous | Blame | View Log

--------------------------------------------------------------------------------
--	File name : spi_slave.vhd
--------------------------------------------------------------------------------
--	Copyright (C) 2015 Donna Whisnant/Dewtronics.
--	Contact: http://www.dewtronics.com/
--
--	This file may be used under the terms of the GNU Lesser General Public License
--	version 3.0 as published by the Free Software Foundation and appearing
--	in the files lgpl-3.0.txt/gpl-3.0.txt included in the packaging of this file.
--	Please review the following information to ensure the GNU Lesser General
--	Public License version 3.0 requirements will be met:
--	https://www.gnu.org/licenses/lgpl-3.0.html
--	Attribution requested, but not required.
--
--	Target Device: Xilinx Spartan-6 XC6SLX9-2-TQG144
--		Using Numato Mimas Spartan 6 FPGA Development Board
--		http://numato.com/mimas-spartan-6-fpga-development-board.html
--
--
--	SPI Slave Entity for TimerOCD
--
--------------------------------------------------------------------------------
 
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;
use IEEE.std_logic_unsigned.all;
 
library UNISIM;
use UNISIM.VCOMPONENTS.all;
 
entity spi_slave is
	generic (
		cpol	: STD_LOGIC := '0';		--spi clock polarity mode
		cpha	: STD_LOGIC := '0';		--spi clock phase mode
		d_width	: INTEGER := 8			--data width in bits
	);
	port (
		sync_clk		: in     STD_LOGIC;		--system clock in for synchronization of the SS signal
		sclk			: in     STD_LOGIC;		--spi clk from master
		reset_n			: in     STD_LOGIC;		--active low reset
		ss_n			: in     STD_LOGIC;		--active low slave select
		mosi			: in     STD_LOGIC;		--master out, slave in
		rrdy			: out    STD_LOGIC := '0';	--receive ready bit
		rx_data			: out    STD_LOGIC_VECTOR(d_width-1 downto 0) := (OTHERS => '0');	--receive register output to logic
		busy			: out    STD_LOGIC := '0';	--busy signal to logic ('1' during transaction)
		miso			: out    STD_LOGIC := 'Z';	--master in, slave out
		tx_load_data	: in     STD_LOGIC_VECTOR(d_width-1 downto 0);		--Tx data to load (synchronous with end of address receive)
		addr			: out    STD_LOGIC_VECTOR(7 downto 0) := (OTHERS => '0');	-- Address to Read or Write (as received from SPI)
		addr_latch		: out    STD_LOGIC := '0';	-- Address Latch Enable Output (Set to True when the address is ready on the output and data should be supplied)
		cmd_load_data	: in     STD_LOGIC_VECTOR(3 downto 0) := (OTHERS => '0')	-- Data nybble to shift out during low nybble of command byte receive
	);
end spi_slave;
 
architecture logic of spi_slave is
	signal mode    : STD_LOGIC;		--groups modes by clock polarity relation to data
	signal clk     : STD_LOGIC;		--clock
	constant nRSB : integer := 4;								-- Number of RxState Bits (used for conversions)
	signal rx_state : STD_LOGIC_VECTOR(nRSB-1 downto 0);		-- Receive state-machine
	signal rxbit_cnt : INTEGER range 0 to d_width-1;			-- Bit count for data received
	constant nTSB : integer := 3;								-- Number of TxState Bits (used for conversions)
	signal tx_state : STD_LOGIC_VECTOR(nTSB-1 downto 0);		-- Transmit state-machine
	constant nTBCSB : integer := 3;								-- Transmit Bit Count State Bits (used for conversions)
	signal txbit_flg : STD_LOGIC_VECTOR(nTBCSB-1 downto 0);		-- Bit count for data sent
	signal wr_add  : STD_LOGIC := '1';	-- address of register to write ('0' = receive, '1' = status)
	signal cmd_add : STD_LOGIC := '0';	-- command or data ('0' = data, '1' = command)
	signal rx_buf  : STD_LOGIC_VECTOR(d_width-2 downto 0) := (OTHERS => '0');	--receiver buffer
	signal tx_buf  : STD_LOGIC_VECTOR(d_width-1 downto 0) := (OTHERS => '0');	-- transmit buffer
	signal cmd_buf : STD_LOGIC_VECTOR(2 downto 0) := (OTHERS => '0');			-- command buffer
	signal out_data : STD_LOGIC;		-- Output data gated to miso
	signal syn_ss_n : STD_LOGIC;		-- Synchronized SS_N signal
	signal syn_mosi : STD_LOGIC;		-- MOSI synchronized to sync_clk
	signal syn_clk_fall : STD_LOGIC;	-- CLK falling edge synchronized to sync_clk
	signal syn_clk_rise : STD_LOGIC;	-- CLK rising edge synchronized to sync_clk
	signal resync_clk : STD_LOGIC_VECTOR(3 downto 0);
	signal resync_ss : STD_LOGIC_VECTOR(1 downto 0);
	signal resync_mosi : STD_LOGIC_VECTOR(1 downto 0);
	signal rrdy_int : STD_LOGIC;		-- Internal rrdy.  Set in the Rx State-Machine.  Used to trigger a delayed rrdy pulse back to the main logic to get data a setup/hold time
	signal rrdy_out : STD_LOGIC;		-- Output rrdy (used to eliminate buffer type)
	signal addr_latch_int : STD_LOGIC;	-- Internal addr_latch.  Set in the Rx State-Machine.  Used to trigger a delayed addr_latch pulse back to the main logic to get data a setup/hold time
	signal addr_latch_out : STD_LOGIC;	-- Output addr_latch (used to eliminate buffer type)
begin
	--adjust clock so writes are on rising edge and reads on falling edge
	mode <= cpol XOR cpha;  --'1' for modes that write on rising edge
	with mode select
		clk <= sclk when '1', NOT sclk when OTHERS;
 
	-- Input synchronization:
	sync_clk_proc:process(sync_clk)
	begin
		if (rising_edge(sync_clk)) then
			syn_ss_n <= resync_ss(0);
			syn_mosi <= resync_mosi(0);
			resync_clk <= resync_clk(2 downto 0) & clk;
			resync_ss <= resync_ss(0) & ss_n;
			resync_mosi <= resync_mosi(0) & mosi;
		end if;
	end process sync_clk_proc;
	syn_clk_fall <= resync_clk(3) and not resync_clk(2);	-- '1' when going from 1 -> 0
	syn_clk_rise <= not resync_clk(2) and resync_clk(1);	-- '1' when going from 0 -> 1
 
	-- output drive:
	with (syn_ss_n OR (NOT reset_n)) select
		miso <= 'Z' when '1', out_data when OTHERS;
 
	busy <= NOT syn_ss_n;  --high during transactions
 
	-- rrdy synchronization:
	--	Since rxspiproc is triggered on the rising_edge of
	--		sync_clk, this process delays reporting it
	--		by 1/2 clock cycle, giving the data time to
	--		get stored:
	rrdyproc:process(syn_ss_n, sync_clk, reset_n)
	begin
		if ((syn_ss_n = '1') OR (reset_n = '0')) then
			rrdy_out <= '0';
		elsif (falling_edge(sync_clk)) then
			if (rrdy_out = '0') then
				rrdy_out <= rrdy_int;
			end if;
		end if;
	end process rrdyproc;
	rrdy <= rrdy_out;
 
	-- addr_latch synchronization:
	--	Since rxspiproc is triggered on the rising_edge of
	--		sync clk, this process delays reporting it
	--		by 1/2 clock cycle, giving the data time to
	--		get stored:
	addrlatchproc:process(syn_ss_n, sync_clk, reset_n)
	begin
		if ((syn_ss_n = '1') OR (reset_n = '0')) then
			addr_latch_out <= '0';
		elsif (falling_edge(sync_clk)) then
			if (addr_latch_out = '0') then
				addr_latch_out <= addr_latch_int;
			end if;
		end if;
	end process addrlatchproc;
	addr_latch <= addr_latch_out;
 
	-- rxspiproc: Receives incoming data
	rxspiproc:process(syn_ss_n, syn_clk_fall, reset_n)
	begin
		if ((syn_ss_n = '1') OR (reset_n = '0')) then
			rx_state <= (OTHERS => '0');
			rx_buf <= (OTHERS => '0');
			rxbit_cnt <= d_width-1;
			rrdy_int <= '0';
			addr_latch_int <= '0';
		elsif (falling_edge(syn_clk_fall)) then
			case CONV_INTEGER(rx_state) is
				when 0 =>
					--read/write mode ('0' for write, '1' for read)
					wr_add <= syn_mosi;
					addr(7) <= syn_mosi;
					rx_state <= CONV_STD_LOGIC_VECTOR(1, nRSB);
				when 1 =>
					--cmd/data mode ('0' for data, '1' for cmd)
					cmd_add <= syn_mosi;
					addr(6) <= syn_mosi;
					rx_state <= CONV_STD_LOGIC_VECTOR(3, nRSB);
				when 3 =>
					addr(5) <= syn_mosi;
					rx_state <= CONV_STD_LOGIC_VECTOR(2, nRSB);
				when 2 =>
					addr(4) <= syn_mosi;
					rx_state <= CONV_STD_LOGIC_VECTOR(6, nRSB);
				when 6 =>
					addr(3) <= syn_mosi;
					rx_state <= CONV_STD_LOGIC_VECTOR(7, nRSB);
				when 7 =>
					addr(2) <= syn_mosi;
					rx_state <= CONV_STD_LOGIC_VECTOR(5, nRSB);
				when 5 =>
					addr(1) <= syn_mosi;
					rx_state <= CONV_STD_LOGIC_VECTOR(4, nRSB);
				when 4 =>
					addr(0) <= syn_mosi;
					if (cmd_add = '1') then
						rx_state <= CONV_STD_LOGIC_VECTOR(13, nRSB);
						if (wr_add = '0') then
							rrdy_int <= '1';
						end if;
					else
						addr_latch_int <= '1';
						rx_state <= CONV_STD_LOGIC_VECTOR(12, nRSB);
					end if;
				when 12 =>
					if (rxbit_cnt = 0) then
						if (wr_add = '0') then
							rx_data <= rx_buf & syn_mosi;
							rrdy_int <= '1';
						end if;
						rx_state <= CONV_STD_LOGIC_VECTOR(13, nRSB);
					else
						if (wr_add = '0') then
							rx_buf <= rx_buf(rx_buf'HIGH-1 downto 0) & syn_mosi;
						end if;
						rxbit_cnt <= (rxbit_cnt - 1);
						-- Stay in this state until all bits are received
					end if;
				when 13 =>
					-- Stay in this state.  The SS_n signal will release us
				when OTHERS =>
			end case;
		end if;
	end process rxspiproc;
 
 
	-- txspiproc: Transmits outgoing data
	--	Note: Commented out code for cmd_buf/cmd_load_data and
	--		tx_buf/tx_load_data can be used to determine the point
	--		where data must be ready/available from the main TimerOCD
	--		relative to the SPI traffic.  This allows for faster SPI
	--		clock rates by only requiring the first couple of bits and/or
	--		first byte of data to be valid when the SPI clock begins
	--		shifting it out.  Since several memory read operations are
	--		required to obtain the data needed to be sent, this allows
	--		for several SPI clock cycle times before that data reading
	--		must be completed.
	txspiproc:process(syn_ss_n, syn_clk_rise, reset_n)
	begin
		if ((syn_ss_n = '1') OR (reset_n = '0')) then
			tx_state <= (OTHERS => '0');
			tx_buf <= (OTHERS => '0');
			txbit_flg <= (OTHERS => '0');
			out_data <= '0';
		elsif (rising_edge(syn_clk_rise)) then
			case CONV_INTEGER(tx_state) is
				when 0 =>
					tx_state <= CONV_STD_LOGIC_VECTOR(1, nTSB);
				when 1 =>
					tx_state <= CONV_STD_LOGIC_VECTOR(3, nTSB);
				when 3 =>
					tx_state <= CONV_STD_LOGIC_VECTOR(2, nTSB);
				when 2 =>
					out_data <= cmd_load_data(3);
--					cmd_buf <= cmd_load_data(2 downto 0);
					tx_state <= CONV_STD_LOGIC_VECTOR(6, nTSB);
				when 6 =>
--					out_data <= cmd_buf(2);
					out_data <= cmd_load_data(2);
					cmd_buf <= cmd_load_data(2 downto 0);
					tx_state <= CONV_STD_LOGIC_VECTOR(7, nTSB);
				when 7 =>
					out_data <= cmd_buf(1);
					tx_state <= CONV_STD_LOGIC_VECTOR(5, nTSB);
				when 5 =>
					out_data <= cmd_buf(0);
					tx_state <= CONV_STD_LOGIC_VECTOR(4, nTSB);
				when 4 =>
					if (cmd_add='0') then
						case CONV_INTEGER(txbit_flg) is
							when 0 =>
								out_data <= tx_load_data(d_width-1);
--								tx_buf <= tx_load_data(d_width-2 downto 0) & "0";
								txbit_flg <= CONV_STD_LOGIC_VECTOR(1, nTBCSB);
							when 1 =>
								out_data <= tx_load_data(d_width-2);
--								tx_buf <= tx_load_data(d_width-3 downto 0) & "00";
								txbit_flg <= CONV_STD_LOGIC_VECTOR(3, nTBCSB);
							when 3 =>
								out_data <= tx_load_data(d_width-3);
--								tx_buf <= tx_load_data(d_width-4 downto 0) & "000";
								txbit_flg <= CONV_STD_LOGIC_VECTOR(2, nTBCSB);
							when 2 =>
								out_data <= tx_load_data(d_width-4);
--								tx_buf <= tx_load_data(d_width-5 downto 0) & "0000";
								txbit_flg <= CONV_STD_LOGIC_VECTOR(6, nTBCSB);
							when 6 =>
								out_data <= tx_load_data(d_width-5);
--								tx_buf <= tx_load_data(d_width-6 downto 0) & "00000";
								txbit_flg <= CONV_STD_LOGIC_VECTOR(7, nTBCSB);
							when 7 =>
								out_data <= tx_load_data(d_width-6);
--								tx_buf <= tx_load_data(d_width-7 downto 0) & "000000";
								txbit_flg <= CONV_STD_LOGIC_VECTOR(5, nTBCSB);
							when 5 =>
								out_data <= tx_load_data(d_width-7);
								tx_buf <= tx_load_data(d_width-8 downto 0) & "0000000";
								txbit_flg <= CONV_STD_LOGIC_VECTOR(4, nTBCSB);
							when 4 =>
								out_data <= tx_buf(tx_buf'HIGH);
								tx_buf <= tx_buf(tx_buf'HIGH-1 downto 0) & "0";
								-- Stay here and shift out remaining data.  SS_n will release us
							when OTHERS =>
						end case;
					else
						out_data <= '0';
					end if;
					-- stay in this state shifting data out
				when OTHERS =>							-- There are no other states, but keep ghdl happy
			end case;
		end if;
	end process txspiproc;
end logic;
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.