Line 1... |
Line 1... |
-- #################################################################################################
|
-- #################################################################################################
|
-- # << NEORV32 - Two-Wire Interface Controller (TWI) >> #
|
-- # << NEORV32 - Two-Wire Interface Controller (TWI) >> #
|
-- # ********************************************************************************************* #
|
-- # ********************************************************************************************* #
|
-- # Supports START and STOP conditions, 8 bit data + ACK/NACK transfers and clock stretching. #
|
-- # Supports START and STOP conditions, 8 bit data + ACK/NACK transfers and clock stretching. #
|
-- # Supports ACKs by the constroller. No multi-controller support and no peripheral mode support #
|
-- # Supports ACKs by the controller. No multi-controller support and no peripheral mode support #
|
-- # yet. Interrupt: TWI_transfer_done #
|
-- # yet. Interrupt: TWI_idle #
|
-- # ********************************************************************************************* #
|
-- # ********************************************************************************************* #
|
-- # BSD 3-Clause License #
|
-- # BSD 3-Clause License #
|
-- # #
|
-- # #
|
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. #
|
-- # Copyright (c) 2021, Stephan Nolting. All rights reserved. #
|
-- # #
|
-- # #
|
Line 98... |
Line 98... |
signal twi_clk_halt : std_ulogic;
|
signal twi_clk_halt : std_ulogic;
|
|
|
-- twi transceiver core --
|
-- twi transceiver core --
|
signal ctrl : std_ulogic_vector(7 downto 0); -- unit's control register
|
signal ctrl : std_ulogic_vector(7 downto 0); -- unit's control register
|
signal arbiter : std_ulogic_vector(2 downto 0);
|
signal arbiter : std_ulogic_vector(2 downto 0);
|
signal twi_bitcnt : std_ulogic_vector(3 downto 0);
|
signal bitcnt : std_ulogic_vector(3 downto 0);
|
signal twi_rtx_sreg : std_ulogic_vector(8 downto 0); -- main rx/tx shift reg
|
signal rtx_sreg : std_ulogic_vector(8 downto 0); -- main rx/tx shift reg
|
|
|
-- tri-state I/O --
|
-- tri-state I/O --
|
signal twi_sda_i_ff0, twi_sda_i_ff1 : std_ulogic; -- sda input sync
|
signal twi_sda_i_ff0, twi_sda_i_ff1 : std_ulogic; -- sda input sync
|
signal twi_scl_i_ff0, twi_scl_i_ff1 : std_ulogic; -- sda input sync
|
signal twi_scl_i_ff0, twi_scl_i_ff1 : std_ulogic; -- sda input sync
|
signal twi_sda_i, twi_sda_o : std_ulogic;
|
signal twi_sda_i, twi_sda_o : std_ulogic;
|
Line 140... |
Line 140... |
data_o(ctrl_twi_prsc1_c) <= ctrl(ctrl_twi_prsc1_c);
|
data_o(ctrl_twi_prsc1_c) <= ctrl(ctrl_twi_prsc1_c);
|
data_o(ctrl_twi_prsc2_c) <= ctrl(ctrl_twi_prsc2_c);
|
data_o(ctrl_twi_prsc2_c) <= ctrl(ctrl_twi_prsc2_c);
|
data_o(ctrl_twi_mack_c) <= ctrl(ctrl_twi_mack_c);
|
data_o(ctrl_twi_mack_c) <= ctrl(ctrl_twi_mack_c);
|
data_o(ctrl_twi_cksten_c) <= ctrl(ctrl_twi_cksten_c);
|
data_o(ctrl_twi_cksten_c) <= ctrl(ctrl_twi_cksten_c);
|
--
|
--
|
data_o(ctrl_twi_ack_c) <= not twi_rtx_sreg(0);
|
data_o(ctrl_twi_ack_c) <= not rtx_sreg(0);
|
data_o(ctrl_twi_busy_c) <= arbiter(1) or arbiter(0);
|
data_o(ctrl_twi_busy_c) <= arbiter(1) or arbiter(0);
|
else -- twi_rtx_addr_c =>
|
else -- twi_rtx_addr_c =>
|
data_o(7 downto 0) <= twi_rtx_sreg(8 downto 1);
|
data_o(7 downto 0) <= rtx_sreg(8 downto 1);
|
|
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
end if;
|
end process rw_access;
|
end process rw_access;
|
Line 156... |
Line 156... |
-- Clock Generation -----------------------------------------------------------------------
|
-- Clock Generation -----------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- -------------------------------------------------------------------------------------------
|
-- clock generator enable --
|
-- clock generator enable --
|
clkgen_en_o <= ctrl(ctrl_twi_en_c);
|
clkgen_en_o <= ctrl(ctrl_twi_en_c);
|
|
|
-- main twi clock select --
|
-- twi clock select --
|
twi_clk <= clkgen_i(to_integer(unsigned(ctrl(ctrl_twi_prsc2_c downto ctrl_twi_prsc0_c))));
|
twi_clk <= clkgen_i(to_integer(unsigned(ctrl(ctrl_twi_prsc2_c downto ctrl_twi_prsc0_c))));
|
|
|
-- generate four non-overlapping clock ticks at twi_clk/4 --
|
-- generate four non-overlapping clock ticks at twi_clk/4 --
|
clock_phase_gen: process(clk_i)
|
clock_phase_gen: process(clk_i)
|
begin
|
begin
|
if rising_edge(clk_i) then
|
if rising_edge(clk_i) then
|
if (arbiter(2) = '0') or (arbiter = "100") then -- offline or idle
|
if (arbiter(2) = '0') or (arbiter = "100") then -- offline or idle
|
twi_phase_gen <= "0001"; -- make sure to start with a new phase, 0,1,2,3 stepping
|
twi_phase_gen <= "0001"; -- make sure to start with a new phase, bit 0,1,2,3 stepping
|
elsif (twi_clk = '1') and (twi_clk_halt = '0') then -- enabled and no clock stretching detected
|
elsif (twi_clk = '1') and (twi_clk_halt = '0') then -- enabled and no clock stretching detected
|
twi_phase_gen <= twi_phase_gen(2 downto 0) & twi_phase_gen(3); -- shift left
|
twi_phase_gen <= twi_phase_gen(2 downto 0) & twi_phase_gen(3); -- rotate left
|
end if;
|
end if;
|
end if;
|
end if;
|
end process clock_phase_gen;
|
end process clock_phase_gen;
|
|
|
|
-- TWI bus signals are set/sampled using 4 clock phases --
|
twi_clk_phase(0) <= twi_phase_gen(0) and twi_clk; -- first step
|
twi_clk_phase(0) <= twi_phase_gen(0) and twi_clk; -- first step
|
twi_clk_phase(1) <= twi_phase_gen(1) and twi_clk;
|
twi_clk_phase(1) <= twi_phase_gen(1) and twi_clk;
|
twi_clk_phase(2) <= twi_phase_gen(2) and twi_clk;
|
twi_clk_phase(2) <= twi_phase_gen(2) and twi_clk;
|
twi_clk_phase(3) <= twi_phase_gen(3) and twi_clk; -- last step
|
twi_clk_phase(3) <= twi_phase_gen(3) and twi_clk; -- last step
|
|
|
Line 195... |
Line 196... |
irq_o <= '1';
|
irq_o <= '1';
|
else
|
else
|
irq_o <= '0';
|
irq_o <= '0';
|
end if;
|
end if;
|
|
|
-- defaults --
|
|
arbiter(2) <= ctrl(ctrl_twi_en_c); -- still activated?
|
|
|
|
-- serial engine --
|
-- serial engine --
|
-- TWI bus signals are set/sampled using 4 clock phases
|
arbiter(2) <= ctrl(ctrl_twi_en_c); -- still activated?
|
case arbiter is
|
case arbiter is
|
|
|
when "100" => -- IDLE: waiting for requests, bus might be still claimed by this controller if no STOP condition was generated
|
when "100" => -- IDLE: waiting for requests, bus might be still claimed by this controller if no STOP condition was generated
|
twi_bitcnt <= (others => '0');
|
bitcnt <= (others => '0');
|
if (wr_en = '1') then
|
if (wr_en = '1') then
|
if (addr = twi_ctrl_addr_c) then
|
if (addr = twi_ctrl_addr_c) then
|
if (data_i(ctrl_twi_start_c) = '1') then -- issue START condition
|
if (data_i(ctrl_twi_start_c) = '1') then -- issue START condition
|
arbiter(1 downto 0) <= "01";
|
arbiter(1 downto 0) <= "01";
|
elsif (data_i(ctrl_twi_stop_c) = '1') then -- issue STOP condition
|
elsif (data_i(ctrl_twi_stop_c) = '1') then -- issue STOP condition
|
arbiter(1 downto 0) <= "10";
|
arbiter(1 downto 0) <= "10";
|
end if;
|
end if;
|
elsif (addr = twi_rtx_addr_c) then -- start a data transmission
|
elsif (addr = twi_rtx_addr_c) then -- start a data transmission
|
-- one bit extra for ack, issued by controller if ctrl_twi_mack_c is set,
|
-- one bit extra for ack, issued by controller if ctrl_twi_mack_c is set,
|
-- sampled from peripheral if ctrl_twi_mack_c is cleared
|
-- sampled from peripheral if ctrl_twi_mack_c is cleared
|
twi_rtx_sreg <= data_i(7 downto 0) & (not ctrl(ctrl_twi_mack_c));
|
rtx_sreg <= data_i(7 downto 0) & (not ctrl(ctrl_twi_mack_c));
|
arbiter(1 downto 0) <= "11";
|
arbiter(1 downto 0) <= "11";
|
end if;
|
end if;
|
end if;
|
end if;
|
|
|
when "101" => -- START: generate START condition
|
when "101" => -- START: generate START condition
|
if (twi_clk_phase(0) = '1') then
|
if (twi_clk_phase(0) = '1') then
|
twi_sda_o <= '1';
|
twi_sda_o <= '1';
|
elsif (twi_clk_phase(1) = '1') then
|
elsif (twi_clk_phase(1) = '1') then
|
twi_sda_o <= '0';
|
twi_sda_o <= '0';
|
end if;
|
end if;
|
|
--
|
if (twi_clk_phase(0) = '1') then
|
if (twi_clk_phase(0) = '1') then
|
twi_scl_o <= '1';
|
twi_scl_o <= '1';
|
elsif (twi_clk_phase(3) = '1') then
|
elsif (twi_clk_phase(3) = '1') then
|
twi_scl_o <= '0';
|
twi_scl_o <= '0';
|
arbiter(1 downto 0) <= "00"; -- go back to IDLE
|
arbiter(1 downto 0) <= "00"; -- go back to IDLE
|
Line 240... |
Line 238... |
twi_sda_o <= '0';
|
twi_sda_o <= '0';
|
elsif (twi_clk_phase(3) = '1') then
|
elsif (twi_clk_phase(3) = '1') then
|
twi_sda_o <= '1';
|
twi_sda_o <= '1';
|
arbiter(1 downto 0) <= "00"; -- go back to IDLE
|
arbiter(1 downto 0) <= "00"; -- go back to IDLE
|
end if;
|
end if;
|
|
--
|
if (twi_clk_phase(0) = '1') then
|
if (twi_clk_phase(0) = '1') then
|
twi_scl_o <= '0';
|
twi_scl_o <= '0';
|
elsif (twi_clk_phase(1) = '1') then
|
elsif (twi_clk_phase(1) = '1') then
|
twi_scl_o <= '1';
|
twi_scl_o <= '1';
|
end if;
|
end if;
|
|
|
when "111" => -- TRANSMISSION: transmission in progress
|
when "111" => -- TRANSMISSION: transmission in progress
|
if (twi_clk_phase(0) = '1') then
|
if (twi_clk_phase(0) = '1') then
|
twi_bitcnt <= std_ulogic_vector(unsigned(twi_bitcnt) + 1);
|
bitcnt <= std_ulogic_vector(unsigned(bitcnt) + 1);
|
twi_scl_o <= '0';
|
twi_scl_o <= '0';
|
twi_sda_o <= twi_rtx_sreg(8); -- MSB first
|
twi_sda_o <= rtx_sreg(8); -- MSB first
|
elsif (twi_clk_phase(1) = '1') then -- first half + second half of valid data strobe
|
elsif (twi_clk_phase(1) = '1') then -- first half + second half of valid data strobe
|
twi_scl_o <= '1';
|
twi_scl_o <= '1';
|
elsif (twi_clk_phase(3) = '1') then
|
elsif (twi_clk_phase(3) = '1') then
|
twi_rtx_sreg <= twi_rtx_sreg(7 downto 0) & twi_sda_i_ff1; -- sample and shift left
|
rtx_sreg <= rtx_sreg(7 downto 0) & twi_sda_i_ff1; -- sample and shift left
|
twi_scl_o <= '0';
|
twi_scl_o <= '0';
|
end if;
|
end if;
|
|
--
|
if (twi_bitcnt = "1010") then -- 8 data bits + 1 bit for ACK + 1 tick delay
|
if (bitcnt = "1010") then -- 8 data bits + 1 bit for ACK + 1 tick delay
|
arbiter(1 downto 0) <= "00"; -- go back to IDLE
|
arbiter(1 downto 0) <= "00"; -- go back to IDLE
|
end if;
|
end if;
|
|
|
when others => -- "0--" OFFLINE: TWI deactivated
|
when others => -- "0--" OFFLINE: TWI deactivated
|
twi_sda_o <= '1';
|
twi_sda_o <= '1';
|
twi_scl_o <= '1';
|
twi_scl_o <= '1';
|
arbiter <= ctrl(ctrl_twi_en_c) & "00"; -- stay here, go to idle when activated
|
arbiter(1 downto 0) <= "00"; -- stay here, go to idle when activated
|
|
|
end case;
|
end case;
|
end if;
|
end if;
|
end process twi_rtx_unit;
|
end process twi_rtx_unit;
|
|
|