library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; library work; use work.usefuls.ALL; -- The first byte is used for setup. -- It is composed of a -R/W flag bit and the following 7 bits addresses. entity SPI2Internal is generic ( bus_width : integer := 8; address_width : integer := 8; spi_slave_devices : integer := 1); port ( clk : in std_logic; -- external SPI spi_cs_n : in std_logic; spi_clk : in std_logic; spi_mosi : in std_logic; spi_miso : out std_logic; spi_slave_cs_n : out std_logic_vector(spi_slave_devices-1 downto 0); spi_slave_miso : in std_logic_vector(spi_slave_devices-1 downto 0); -- internal bus internal_cs : out std_logic; internal_m2d : out std_logic_vector(bus_width-1 downto 0); internal_d2m : in std_logic_vector(bus_width-1 downto 0); internal_dir : out std_logic; -- master to device => '1', device to master => '0' internal_addr : out std_logic_vector(address_width-1 downto 0); internal_done : in std_logic); -- complete write / valid output end SPI2Internal; architecture Behavioral of SPI2Internal is constant DIR_D2M : std_logic := '0'; constant DIR_M2D : std_logic := '1'; type state_type is (IDLE, SETUP, DATA, SLAVE); signal current_state : state_type := IDLE; signal setup_register : std_logic_vector(7 downto 0); signal shift_register : std_logic_vector(bus_width downto 0); signal shift_count : std_logic_vector(log2_ceil(bus_width) - 1 downto 0); signal spi_cs_n_buf : std_logic; signal spi_clk_buf, spi_clk_buf2 : std_logic; signal spi_mosi_buf : std_logic; signal spi_miso_hold : std_logic; signal internal_cs_active : boolean; signal internal_dir_buf : std_logic; signal spi_slave_address : std_logic_vector(log2_floor(spi_slave_devices - 1) downto 0); begin process(clk) variable shift_register_next : std_logic_vector(shift_register'range); begin if clk'event and clk = '1' then spi_cs_n_buf <= spi_cs_n; if spi_cs_n_buf = '1' then spi_clk_buf <= '1'; spi_clk_buf2 <= '1'; else spi_clk_buf <= spi_clk; spi_clk_buf2 <= spi_clk_buf; end if; spi_mosi_buf <= spi_mosi; shift_register_next := shift_register; if spi_clk_buf = '0' then if current_state = SETUP and shift_count = conv_std_logic_vector(0, shift_count'length) then for i in shift_register_next'high downto 1 loop if i mod 2 = 0 then shift_register_next(i) := '1'; else shift_register_next(i) := '0'; end if; end loop; elsif internal_cs_active and internal_done = '1' then shift_register_next(shift_register_next'high downto 1) := internal_d2m; elsif spi_clk_buf2 = '1' then shift_register_next(shift_register_next'high downto 1) := shift_register(shift_register_next'high - 1 downto 0); end if; shift_register_next(0) := spi_mosi_buf; end if; shift_register <= shift_register_next; if current_state = SETUP and spi_clk_buf = '0' then if spi_clk_buf2 = '1' then setup_register(setup_register'high downto 1) <= setup_register(setup_register'high - 1 downto 0); else setup_register(setup_register'high downto 1) <= setup_register(setup_register'high downto 1); end if; setup_register(0) <= spi_mosi_buf; elsif current_state = DATA and internal_cs_active and internal_done = '1' and ((setup_register(setup_register'high) = '0' and internal_dir_buf = DIR_D2M) or (setup_register(setup_register'high) = '1' and internal_dir_buf = DIR_M2D)) then setup_register(setup_register'high) <= setup_register(setup_register'high); if setup_register(setup_register'high - 1 downto 0) = conv_std_logic_vector(2**address_width - 1, setup_register'length - 1) then setup_register(setup_register'high - 1 downto 0) <= (others => '0'); else setup_register(setup_register'high - 1 downto 0) <= setup_register(setup_register'high - 1 downto 0) + 1; end if; else setup_register <= setup_register; end if; end if; end process; process(clk) begin if clk'event and clk = '1' then if spi_cs_n_buf = '1' then current_state <= IDLE; else case current_state is when IDLE => current_state <= SETUP; shift_count <= (others => '0'); when SETUP => if spi_clk_buf2 = '0' and spi_clk_buf = '1' then if shift_count = conv_std_logic_vector(setup_register'high, shift_count'length) then if setup_register(setup_register'high - 1 downto 0) <= conv_std_logic_vector(2**address_width - 1, setup_register'length - 1) then current_state <= DATA; else current_state <= SLAVE; end if; shift_count <= (others => '0'); else current_state <= current_state; shift_count <= shift_count + 1; end if; else current_state <= current_state; shift_count <= shift_count; end if; when DATA => current_state <= current_state; if spi_clk_buf2 = '0' and spi_clk_buf = '1' then if shift_count = conv_std_logic_vector(bus_width - 1, shift_count'length) then shift_count <= (others => '0'); else shift_count <= shift_count + 1; end if; else shift_count <= shift_count; end if; when SLAVE => current_state <= current_state; shift_count <= shift_count; end case; end if; end if; end process; spi_slave_address <= setup_register(spi_slave_address'range); gen_many_slaves : if spi_slave_cs_n'high > 0 generate gen : for i in spi_slave_cs_n'high - 1 downto 0 generate spi_slave_cs_n(i) <= '0' when current_state = SLAVE and spi_slave_address = conv_std_logic_vector(i, spi_slave_address'length) else '1'; end generate; end generate; spi_slave_cs_n(spi_slave_cs_n'high) <= '0' when current_state = SLAVE and spi_slave_address >= conv_std_logic_vector(spi_slave_cs_n'high, spi_slave_address'length) else '1'; process(clk, spi_clk) begin if spi_clk = '1' then spi_miso_hold <= spi_miso_hold; elsif clk'event and clk = '1' then spi_miso_hold <= shift_register(shift_register'high); end if; end process; process(current_state, spi_slave_address, spi_miso_hold, spi_slave_miso) variable tmp : std_logic; begin tmp := spi_miso_hold; if current_state = IDLE then tmp := 'Z'; elsif current_state = SLAVE then tmp := spi_slave_miso(spi_slave_miso'high); for i in spi_slave_miso'range loop if spi_slave_address = conv_std_logic_vector(i, spi_slave_address'length) then tmp := spi_slave_miso(i); end if; end loop; end if; spi_miso <= tmp; end process; process(clk) begin if clk'event and clk = '1' then if internal_cs_active and internal_done = '1' then internal_cs_active <= false; internal_dir_buf <= internal_dir_buf; elsif current_state = DATA then if shift_count = conv_std_logic_vector(0, shift_count'length) and spi_clk_buf2 = '1' and spi_clk_buf = '0' then internal_cs_active <= true; internal_dir_buf <= DIR_D2M; elsif shift_count = conv_std_logic_vector(bus_width - 1, shift_count'length) and spi_clk_buf2 = '0' and spi_clk_buf = '1' and setup_register(setup_register'high) = '1' then internal_cs_active <= true; internal_dir_buf <= DIR_M2D; else internal_cs_active <= internal_cs_active; internal_dir_buf <= internal_dir_buf; end if; else internal_cs_active <= false; internal_dir_buf <= internal_dir_buf; end if; end if; end process; internal_cs <= '1' when internal_cs_active else '0'; internal_addr <= setup_register(address_width - 1 downto 0) when internal_cs_active else (others => 'Z'); internal_dir <= internal_dir_buf when internal_cs_active else 'Z'; internal_m2d <= shift_register(shift_register'high - 1 downto 0) when internal_cs_active else (others => 'Z'); end Behavioral;