--! @author David Bailey (d.bailey@cern.ch) -- comf.include: UQDS_specifics_pkg.vhd -- comf.vhdl.work: uqdslib library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_unsigned.all; use ieee.numeric_std.all; Library UQDSLib; use UQDSLib.UQDS_specifics_pkg.all; --! @brief Simple 8-bit fixed length SPI master --! @details This VHDL code is intended to provide a basic data I/O --! interface to easily interface with standard SPI-based peripherals. --! Its main purpose is to clock data in/out. It does *not* contain --! arbitratrion nor chip-select handling! entity spi_master is generic( --! Will divide the FPGA-Clock by this value for the SPI clock clkdiv : natural := 40; clk_idle : std_logic := '1' ); port( --! Resets the SPI port. Will stop it mid-transition. --! Forces data_rx to 0. rst : in std_logic; --! FPGA Clock to run internal logic at clk : in std_logic; spi_control_to : in spi_control_to_t; spi_control_from : out spi_control_from_t; pin_mosi : out std_logic; pin_miso : in std_logic; pin_clk : out std_logic ); end; architecture rtl of spi_master is type spi_state_t is ( IDLE, HIGH_CLK, LOW_CLK ); signal spi_state : spi_state_t := IDLE; signal clk_divider : integer range 0 to clkdiv/2 ; signal bit_cnt : integer range 0 to 7; signal data_rx_fragment : std_logic_vector(7 downto 0); signal data_tx_fragment : std_logic_vector(6 downto 0); signal byte_tx_complete_i : std_logic := '0'; signal byte_rx_complete_i : std_logic := '0'; begin spi_control_from.byte_tx_complete <= byte_tx_complete_i; spi_control_from.byte_rx_complete <= byte_rx_complete_i; spi_transmission: process(clk) begin if(rising_edge(clk)) then byte_tx_complete_i <= '0'; byte_rx_complete_i <= '0'; --! Reset clause, the rest of the code won't get to run --! while reset asserts if(rst = '1') then spi_state <= IDLE; clk_divider <= 0; bit_cnt <= 0; data_rx_fragment <= (others => '0'); data_tx_fragment <= (others => '0'); spi_control_from.data_rx <= (others => '0'); byte_tx_complete_i <= '0'; byte_rx_complete_i <= '0'; pin_mosi <= '0'; pin_clk <= clk_idle; elsif(clk_divider > 0) then clk_divider <= clk_divider - 1; else case spi_state is when IDLE => if(spi_control_to.send_request) then data_tx_fragment <= spi_control_to.data_tx(6 downto 0); data_rx_fragment <= (others => '0'); clk_divider <= clkdiv / 2; bit_cnt <= 7; pin_clk <= not clk_idle; pin_mosi <= spi_control_to.data_tx(spi_control_to.data_tx'left); spi_state <= HIGH_CLK; byte_tx_complete_i <= '1'; end if; when HIGH_CLK => pin_clk <= clk_idle; data_rx_fragment <= data_rx_fragment(6 downto 0) & pin_miso; clk_divider <= clkdiv / 2; spi_state <= LOW_CLK; if(bit_cnt = 0) then spi_control_from.data_rx <= data_rx_fragment(6 downto 0) & pin_miso; byte_rx_complete_i <= '1'; spi_state <= IDLE; else bit_cnt <= bit_cnt - 1; end if; when LOW_CLK => pin_clk <= not clk_idle; pin_mosi <= data_tx_fragment(data_tx_fragment'left); data_tx_fragment <= data_tx_fragment(data_tx_fragment'left-1 downto 0) & '0'; clk_divider <= clkdiv / 2; spi_state <= HIGH_CLK; end case; end if; end if; end process; end architecture;