diff --git a/modules/common/Manifest.py b/modules/common/Manifest.py index 7f356aeca3c09d3af48b70f2b1586041c81655de..cd9764810476add8c1221fd4cfe130eef716c429 100644 --- a/modules/common/Manifest.py +++ b/modules/common/Manifest.py @@ -37,4 +37,5 @@ files = [ "gc_async_counter_diff.vhd", "gc_sync_word_wr.vhd", "gc_sync_word_rd.vhd", + "gc_simple_spi_master.vhd" ]; diff --git a/modules/common/gc_simple_spi_master.vhd b/modules/common/gc_simple_spi_master.vhd new file mode 100644 index 0000000000000000000000000000000000000000..5fd387e917bd1308f68faf33198ca579750a4614 --- /dev/null +++ b/modules/common/gc_simple_spi_master.vhd @@ -0,0 +1,205 @@ +----------------------------------------------------------------------------- +-- Title : SPI Bus Master +-- Project : Simple VME64x FMC Carrier (SVEC) +------------------------------------------------------------------------------- +-- File : gc_simple_spi_master.vhd +-- Author : Tomasz Wlostowski +-- Company : CERN +-- Created : 2011-08-24 +-- Last update: 2013-01-25 +-- Platform : FPGA-generic +-- Standard : VHDL'93 +------------------------------------------------------------------------------- +-- Description: Just a simple SPI master (bus-less). +------------------------------------------------------------------------------- +-- +-- Copyright (c) 2011-2013 CERN / BE-CO-HT +-- +-- This source file is free software; you can redistribute it +-- and/or modify it under the terms of the GNU Lesser General +-- Public License as published by the Free Software Foundation; +-- either version 2.1 of the License, or (at your option) any +-- later version. +-- +-- This source is distributed in the hope that it will be +-- useful, but WITHOUT ANY WARRANTY; without even the implied +-- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +-- PURPOSE. See the GNU Lesser General Public License for more +-- details. +-- +-- You should have received a copy of the GNU Lesser General +-- Public License along with this source; if not, download it +-- from http://www.gnu.org/licenses/lgpl-2.1.html +-- +------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity gc_simple_spi_master is + generic( + -- clock division ratio (SCLK = clk_sys_i / (2 ** g_div_ratio_log2). + g_div_ratio_log2 : integer := 2; + -- number of data bits per transfer + g_num_data_bits : integer := 2); + port ( + clk_sys_i : in std_logic; + rst_n_i : in std_logic; + + -- state of the Chip select line (1 = CS active). External control + -- allows for multi-transfer commands (SPI master itself does not + -- control the state of spi_cs_n_o) + cs_i : in std_logic; + + -- 1: start next transfer (using CPOL, DATA and SEL from the inputs below) + start_i : in std_logic; + + -- Clock polarity: 1: slave clocks in the data on rising SCLK edge, 0: ... + -- on falling SCLK edge + cpol_i : in std_logic; + + -- TX Data input + data_i : in std_logic_vector(g_num_data_bits - 1 downto 0); + + -- 1: data_o contains the result of last read operation. Core is ready to initiate + -- another transfer. + ready_o : out std_logic; + + -- data read from selected slave, valid when ready_o == 1. + data_o : out std_logic_vector(g_num_data_bits - 1 downto 0); + + -- these are obvious + spi_cs_n_o : out std_logic; + spi_sclk_o : out std_logic; + spi_mosi_o : out std_logic; + spi_miso_i : in std_logic + ); + +end gc_simple_spi_master; + +architecture behavioral of gc_simple_spi_master is + + signal divider : unsigned(11 downto 0); + signal tick : std_logic; + + signal sreg : std_logic_vector(g_num_data_bits-1 downto 0); + signal rx_sreg : std_logic_vector(g_num_data_bits-1 downto 0); + + type t_state is (IDLE, TX_CS, TX_DAT1, TX_DAT2, TX_SCK1, TX_SCK2, TX_CS2, TX_GAP); + signal state : t_state; + signal sclk : std_logic; + + signal counter : unsigned(4 downto 0); + +begin -- rtl + + -- Simple clock divder. Produces a 'tick' signal which defines the timing for + -- the main state machine transitions. + p_divide_spi_clock: process(clk_sys_i) + begin + if rising_edge(clk_sys_i) then + if rst_n_i = '0' then + divider <= (others => '0'); + else + if(start_i = '1' or tick = '1') then + divider <= (others => '0'); + else + divider <= divider + 1; + end if; + end if; + end if; + end process; + + tick <= divider(g_div_ratio_log2); + + -- Main state machine. Executes SPI transfers + p_main_fsm: process(clk_sys_i) + begin + if rising_edge(clk_sys_i) then + if rst_n_i = '0' then + state <= IDLE; + sclk <= '0'; + sreg <= (others => '0'); + rx_sreg <= (others => '0'); + spi_mosi_o <= '0'; + counter <= (others => '0'); + else + case state is + -- Waits for start of transfer command + when IDLE => + sclk <= '0'; + counter <= (others => '0'); + if(start_i = '1') then + sreg <= data_i; + state <= TX_CS; + spi_mosi_o <= data_i(sreg'high); + end if; + + -- Generates a gap between the externally asserted Chip Select and + -- the beginning of data transfer + when TX_CS => + if tick = '1' then + state <= TX_DAT1; + end if; + + -- Outputs subsequent bits to MOSI line. + when TX_DAT1 => + if(tick = '1') then + spi_mosi_o <= sreg(sreg'high); + sreg <= sreg(sreg'high-1 downto 0) & '0'; + state <= TX_SCK1; + end if; + + -- Flips the SCLK (active edge) + when TX_SCK1 => + if(tick = '1') then + sclk <= not sclk; + counter <= counter + 1; + state <= TX_DAT2; + end if; + + -- Shifts in bits read from the slave + when TX_DAT2 => + + if(tick = '1') then + rx_sreg <= rx_sreg(rx_sreg'high-1 downto 0) & spi_miso_i; + state <= TX_SCK2; + end if; + + -- Flips the SCLK (inactive edge). Checks if all bits have been + -- transferred. + when TX_SCK2 => + if(tick = '1') then + sclk <= not sclk; + if(counter = g_num_data_bits) then + state <= TX_CS2; + else + state <= TX_DAT1; + end if; + end if; + + -- Generates a gap for de-assertoin of CS line + when TX_CS2 => + if(tick = '1') then + state <= TX_GAP; + data_o <= rx_sreg; + end if; + + when TX_GAP => + if (tick = '1') then + state <= IDLE; + end if; + end case; + end if; + end if; + end process; + + ready_o <= '1' when (state = IDLE and start_i = '0') else '0'; + + -- SCLK polarity control + spi_sclk_o <= sclk xor cpol_i; + spi_cs_n_o <= not cs_i; + +end behavioral; +