Index: common/components/I2C_regs.vhd
===================================================================
--- common/components/I2C_regs.vhd	(nonexistent)
+++ common/components/I2C_regs.vhd	(revision 980)
@@ -0,0 +1,175 @@
+---------------------------------------------------------------------------
+-- (c) 2020 mark watson
+-- I am happy for anyone to use this for non-commercial use.
+-- If my vhdl files are used commercially or otherwise sold,
+-- please contact me for explicit permission at scrameta (gmail).
+-- This applies for source and binary form and derived works.
+---------------------------------------------------------------------------
+
+LIBRARY ieee;
+USE ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+USE IEEE.STD_LOGIC_UNSIGNED.ALL;
+use IEEE.STD_LOGIC_MISC.all;
+
+ENTITY I2C_regs IS
+  generic (
+    SLAVE_ADDR : std_logic_vector(6 downto 0);
+	 regs : integer := 1; -- up to 16
+	 bits : integer := 1
+  );
+  port (
+    scl              : inout std_logic;
+    sda              : inout std_logic;
+    clk              : in    std_logic;
+    rst              : in    std_logic;
+	 
+    -- User interface	 	
+    reg : out std_logic_vector((regs*bits)-1 downto 0)
+);
+END I2C_regs;
+
+ARCHITECTURE vhdl OF I2C_regs IS	
+	signal i2c_write : std_logic;
+	signal i2c_read : std_logic;
+	signal i2c_write_data : std_logic_vector(7 downto 0);
+	signal i2c_read_data : std_logic_vector(7 downto 0);
+
+	signal i2c_addr_next : std_logic_vector(3 downto 0);
+	signal i2c_addr_reg : std_logic_vector(3 downto 0);
+
+	signal i2c_state_next : std_logic_vector(2 downto 0);
+	signal i2c_state_reg : std_logic_vector(2 downto 0);
+	constant I2C_INIT : std_logic_vector(2 downto 0) := "000";
+	constant I2C_READ1 : std_logic_vector(2 downto 0) := "001";
+	constant I2C_READ2 : std_logic_vector(2 downto 0) := "010";
+	constant I2C_WRITE1 : std_logic_vector(2 downto 0) := "011";
+	constant I2C_WRITE2 : std_logic_vector(2 downto 0) := "100";
+	
+	signal reg_next : std_logic_vector((regs*bits)-1 downto 0);
+	signal reg_reg : std_logic_vector((regs*bits)-1 downto 0);
+	
+	function MIN(LEFT, RIGHT: INTEGER) return INTEGER is
+	begin
+	  if LEFT < RIGHT then return LEFT;
+	  else return RIGHT;
+		 end if;
+	  end;	
+	  
+	function IX(r,c : natural) return natural is
+	begin
+     return (r*bits + c);
+	end;	  
+BEGIN
+
+	process(clk,rst)
+	begin
+		if (rst='1') then
+			reg_reg <= (others=>'0');
+			
+			-- i2c
+			i2c_state_reg <= I2C_INIT;
+			i2c_addr_reg <= (others=>'0');
+		elsif (clk'event and clk='1') then
+			reg_reg <= reg_next;
+
+			-- i2c
+			i2c_state_reg <= i2c_state_next;
+			i2c_addr_reg <= i2c_addr_next;
+		end if;
+	end process;
+
+	i2cslave : entity work.I2C_slave
+	generic map (
+		SLAVE_ADDR => SLAVE_ADDR
+	)
+	port map (
+		scl => scl,
+		sda => sda,
+		clk => clk,
+		rst => rst,
+
+		read_req => i2c_read,
+		data_to_master => i2c_read_data,
+		data_valid => i2c_write,
+		data_from_master => i2c_write_data
+	);
+
+	process(
+		reg_reg,
+		i2c_addr_reg,
+		i2c_state_reg,
+		i2c_read,
+		i2c_write,
+		i2c_write_data
+	)
+		variable low_max : integer;
+		variable i2c_addr_int : integer;
+	begin
+		low_max := min(7,bits-1);
+		i2c_addr_int := to_integer(unsigned(i2c_addr_reg));
+	
+		reg_next <= reg_reg;
+
+		i2c_addr_next <= i2c_addr_reg;
+		i2c_state_next <= i2c_state_reg;
+
+		i2c_read_data <= (others=>'0');
+
+		case(i2c_state_reg) is
+		when I2C_INIT =>
+			if (i2c_write='1' and i2c_write_data(7 downto 5)="111") then -- F= write, E= read, bottom 4 bits = reg
+				i2c_addr_next <= i2c_write_data(3 downto 0);
+				if (i2c_write_data(4)='1') then
+					i2c_state_next <= I2C_WRITE1;
+				else
+					i2c_state_next <= I2C_READ1;
+				end if;
+			end if;
+		when I2C_WRITE1 =>
+			if (i2c_write='1') then
+				for i in 0 to regs-1 loop
+					if (i2c_addr_int=i) then			
+						reg_next(IX(i,low_max) downto IX(i,0)) <= i2c_write_data(low_max downto 0);
+					end if;
+				end loop;
+				i2c_state_next <= I2C_WRITE2;
+			end if;
+		when I2C_WRITE2 =>
+			if (i2c_write='1') then
+				for i in 0 to regs-1 loop
+					if (i2c_addr_int=i) then					
+						reg_next(ix(i,bits-1) downto ix(i, 8)) <= i2c_write_data(bits-9 downto 0);
+					end if;
+				end loop;
+				i2c_state_next <= I2C_INIT;
+			end if;
+		when I2C_READ1 =>
+			for i in 0 to regs-1 loop
+				if (i2c_addr_int=i) then
+					i2c_read_data(low_max downto 0) <= reg_reg(ix(i,low_max) downto ix(i,0));
+				end if;
+			end loop;
+			if (i2c_read='1') then
+				i2c_state_next <= I2C_READ2;
+			end if;
+		when I2C_READ2 =>			
+			for i in 0 to regs-1 loop
+				if (i2c_addr_int=i) then
+					i2c_read_data(bits-9 downto 0) <= reg_reg(ix(i,bits-1) downto ix(i,8));
+				end if;
+			end loop;			
+			
+			if (i2c_read='1') then
+				i2c_state_next <= I2C_INIT;
+			end if;
+		when others =>
+			i2c_state_next <= I2C_INIT;
+		end case;
+	end process;
+	
+	reg <= reg_reg;
+	
+end vhdl;
+		
+		
\ No newline at end of file
Index: common/components/I2C_slave.vhd
===================================================================
--- common/components/I2C_slave.vhd	(nonexistent)
+++ common/components/I2C_slave.vhd	(revision 980)
@@ -0,0 +1,305 @@
+------------------------------------------------------------
+-- File      : I2C_slave.vhd
+------------------------------------------------------------
+-- Author    : Peter Samarin <peter.samarin@gmail.com>
+------------------------------------------------------------
+-- Copyright (c) 2016 Peter Samarin
+------------------------------------------------------------
+library ieee;
+use ieee.std_logic_1164.all;
+use ieee.numeric_std.all;
+------------------------------------------------------------
+entity I2C_slave is
+  generic (
+    SLAVE_ADDR : std_logic_vector(6 downto 0));
+  port (
+    scl              : inout std_logic;
+    sda              : inout std_logic;
+    clk              : in    std_logic;
+    rst              : in    std_logic;
+    -- User interface
+    read_req         : out   std_logic;
+    data_to_master   : in    std_logic_vector(7 downto 0);
+    data_valid       : out   std_logic;
+    data_from_master : out   std_logic_vector(7 downto 0));
+end entity I2C_slave;
+------------------------------------------------------------
+architecture arch of I2C_slave is
+  -- this assumes that system's clock is much faster than SCL
+  constant DEBOUNCING_WAIT_CYCLES : integer   := 4;
+  
+  type state_t is (idle, get_address_and_cmd,
+                   answer_ack_start, write,
+                   read, read_ack_start,
+                   read_ack_got_rising, read_stop);
+  -- I2C state management
+  signal state_reg          : state_t              := idle;
+  signal cmd_reg            : std_logic            := '0';
+  signal bits_processed_reg : integer range 0 to 8 := 0;
+  signal continue_reg       : std_logic            := '0';
+
+  signal scl_reg                  : std_logic := '1';
+  signal sda_reg                  : std_logic := '1';
+  signal scl_debounced            : std_logic := '1';
+  signal sda_debounced            : std_logic := '1';
+  
+
+  -- Helpers to figure out next state
+  signal start_reg       : std_logic := '0';
+  signal stop_reg        : std_logic := '0';
+  signal scl_rising_reg  : std_logic := '0';
+  signal scl_falling_reg : std_logic := '0';
+
+  -- Address and data received from master
+  signal addr_reg             : std_logic_vector(6 downto 0) := (others => '0');
+  signal data_reg             : std_logic_vector(6 downto 0) := (others => '0');
+  signal data_from_master_reg : std_logic_vector(7 downto 0) := (others => '0');
+
+  signal scl_prev_reg : std_logic := '1';
+  -- Slave writes on scl
+  signal scl_wen_reg  : std_logic := '0';
+  signal scl_o_reg    : std_logic := '0';
+  signal sda_prev_reg : std_logic := '1';
+  -- Slave writes on sda
+  signal sda_wen_reg  : std_logic := '0';
+  signal sda_o_reg    : std_logic := '0';
+
+  -- User interface
+  signal data_valid_reg     : std_logic                    := '0';
+  signal read_req_reg       : std_logic                    := '0';
+  signal data_to_master_reg : std_logic_vector(7 downto 0) := (others => '0');
+begin
+
+  -- debounce SCL and SDA
+--  SCL_debounce : entity work.debounce
+--    generic map (
+--      WAIT_CYCLES => DEBOUNCING_WAIT_CYCLES)
+--    port map (
+--      clk        => clk,
+--      signal_in  => scl_reg,
+--      signal_out => scl_debounced);
+
+	scl_debounced <= '1' when scl_reg='H' else scl_reg;
+
+--  -- it might not make sense to debounce SDA, since master
+--  -- and slave can both write to it...
+--  SDA_debounce : entity work.debounce
+--    generic map (
+--      WAIT_CYCLES => DEBOUNCING_WAIT_CYCLES)
+--    port map (
+--      clk        => clk,
+--      signal_in  => sda_reg,
+--      signal_out => sda_debounced);
+
+	sda_debounced <= '1' when sda_reg='H' else sda_reg;
+
+  process (clk) is
+  begin
+    if rising_edge(clk) then
+      -- save SCL in registers that are used for debouncing
+      scl_reg <= scl;
+      sda_reg <= sda;
+
+      -- Delay debounced SCL and SDA by 1 clock cycle
+      scl_prev_reg   <= scl_debounced;
+      sda_prev_reg   <= sda_debounced;
+      -- Detect rising and falling SCL
+      scl_rising_reg <= '0';
+      if scl_prev_reg = '0' and scl_debounced = '1' then
+        scl_rising_reg <= '1';
+      end if;
+      scl_falling_reg <= '0';
+      if scl_prev_reg = '1' and scl_debounced = '0' then
+        scl_falling_reg <= '1';
+      end if;
+
+      -- Detect I2C START condition
+      start_reg <= '0';
+      stop_reg  <= '0';
+      if scl_debounced = '1' and scl_prev_reg = '1' and
+        sda_prev_reg = '1' and sda_debounced = '0' then
+        start_reg <= '1';
+        stop_reg  <= '0';
+      end if;
+
+      -- Detect I2C STOP condition
+      if scl_prev_reg = '1' and scl_debounced = '1' and
+        sda_prev_reg = '0' and sda_debounced = '1' then
+        start_reg <= '0';
+        stop_reg  <= '1';
+      end if;
+
+    end if;
+  end process;
+
+  ----------------------------------------------------------
+  -- I2C state machine
+  ----------------------------------------------------------
+  process (clk) is
+  begin
+    if rising_edge(clk) then
+      -- Default assignments
+      sda_o_reg      <= '0';
+      sda_wen_reg    <= '0';
+      -- User interface
+      data_valid_reg <= '0';
+      read_req_reg   <= '0';
+
+      case state_reg is
+
+        when idle =>
+          if start_reg = '1' then
+            state_reg          <= get_address_and_cmd;
+            bits_processed_reg <= 0;
+          end if;
+
+        when get_address_and_cmd =>
+          if scl_rising_reg = '1' then
+            if bits_processed_reg < 7 then
+              bits_processed_reg             <= bits_processed_reg + 1;
+              addr_reg(6-bits_processed_reg) <= sda_debounced;
+            elsif bits_processed_reg = 7 then
+              bits_processed_reg <= bits_processed_reg + 1;
+              cmd_reg            <= sda_debounced;
+            end if;
+          end if;
+
+          if bits_processed_reg = 8 and scl_falling_reg = '1' then
+            bits_processed_reg <= 0;
+            if addr_reg = SLAVE_ADDR then  -- check req address
+              state_reg <= answer_ack_start;
+              if cmd_reg = '1' then  -- issue read request 
+                read_req_reg       <= '1';
+                data_to_master_reg <= data_to_master;
+              end if;
+            else
+              assert false
+                report ("I2C: target/slave address mismatch (data is being sent to another slave).")
+                severity note;
+              state_reg <= idle;
+            end if;
+          end if;
+
+        ----------------------------------------------------
+        -- I2C acknowledge to master
+        ----------------------------------------------------
+        when answer_ack_start =>
+          sda_wen_reg <= '1';
+          sda_o_reg   <= '0';
+          if scl_falling_reg = '1' then
+            if cmd_reg = '0' then
+              state_reg <= write;
+            else
+              state_reg <= read;
+            end if;
+          end if;
+
+        ----------------------------------------------------
+        -- WRITE
+        ----------------------------------------------------
+        when write =>
+          if scl_rising_reg = '1' then
+            bits_processed_reg <= bits_processed_reg + 1;
+            if bits_processed_reg < 7 then
+              data_reg(6-bits_processed_reg) <= sda_debounced;
+            else
+              data_from_master_reg <= data_reg & sda_debounced;
+              data_valid_reg       <= '1';
+            end if;
+          end if;
+
+          if scl_falling_reg = '1' and bits_processed_reg = 8 then
+            state_reg          <= answer_ack_start;
+            bits_processed_reg <= 0;
+          end if;
+
+        ----------------------------------------------------
+        -- READ: send data to master
+        ----------------------------------------------------
+        when read =>
+          sda_wen_reg <= '1';
+          sda_o_reg   <= data_to_master_reg(7-bits_processed_reg);
+          if scl_falling_reg = '1' then
+            if bits_processed_reg < 7 then
+              bits_processed_reg <= bits_processed_reg + 1;
+            elsif bits_processed_reg = 7 then
+              state_reg          <= read_ack_start;
+              bits_processed_reg <= 0;
+            end if;
+          end if;
+
+        ----------------------------------------------------
+        -- I2C read master acknowledge
+        ----------------------------------------------------
+        when read_ack_start =>
+          if scl_rising_reg = '1' then
+            state_reg <= read_ack_got_rising;
+            if sda_debounced = '1' then  -- nack = stop read
+              continue_reg <= '0';
+            else  -- ack = continue read
+              continue_reg       <= '1';
+              read_req_reg       <= '1';  -- request reg byte
+              data_to_master_reg <= data_to_master;
+            end if;
+          end if;
+
+        when read_ack_got_rising =>
+          if scl_falling_reg = '1' then
+            if continue_reg = '1' then
+              if cmd_reg = '0' then
+                state_reg <= write;
+              else
+                state_reg <= read;
+              end if;
+            else
+              state_reg <= read_stop;
+            end if;
+          end if;
+
+        -- Wait for START or STOP to get out of this state
+        when read_stop =>
+          null;
+
+        -- Wait for START or STOP to get out of this state
+        when others =>
+          assert false
+            report ("I2C: error: ended in an impossible state.")
+            severity error;
+          state_reg <= idle;
+      end case;
+
+      --------------------------------------------------------
+      -- Reset counter and state on start/stop
+      --------------------------------------------------------
+      if start_reg = '1' then
+        state_reg          <= get_address_and_cmd;
+        bits_processed_reg <= 0;
+      end if;
+
+      if stop_reg = '1' then
+        state_reg          <= idle;
+        bits_processed_reg <= 0;
+      end if;
+
+      if rst = '1' then
+        state_reg <= idle;
+      end if;
+    end if;
+  end process;
+
+  ----------------------------------------------------------
+  -- I2C interface
+  ----------------------------------------------------------
+  sda <= sda_o_reg when sda_wen_reg = '1' else
+         'Z';
+  scl <= scl_o_reg when scl_wen_reg = '1' else
+         'Z';
+  ----------------------------------------------------------
+  -- User interface
+  ----------------------------------------------------------
+  -- Master writes
+  data_valid       <= data_valid_reg;
+  data_from_master <= data_from_master_reg;
+  -- Master reads
+  read_req         <= read_req_reg;
+end architecture arch;
