| 
    
       ---------------------------------------------------------------------------
 
     | 
  
  
     | 
    
       -- (c) 2018 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;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       -- TALK to PCAL6416A io expander over i2c
 
     | 
  
  
     | 
    
       -- goals: 
 
     | 
  
  
     | 
    
       -- keyboard:
 
     | 
  
  
     | 
    
       -- 	set keyboard 6 lines to output
 
     | 
  
  
     | 
    
       -- 	drive keyboard 6 lines constantly
 
     | 
  
  
     | 
    
       -- 	receive keyboard 2 lines constantly
 
     | 
  
  
     | 
    
       -- 	review if we need pull ups on 2 keyboard inputs
 
     | 
  
  
     | 
    
       -- 	work out which pins in invert on keyboard
 
     | 
  
  
     | 
    
       -- pot:
 
     | 
  
  
     | 
    
       -- 	need to drive to 0 to reset
 
     | 
  
  
     | 
    
       -- 	otherwise need to sit at high impedence input
 
     | 
  
  
     | 
    
       -- 	so a single bit input is fed in to do this...
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       ENTITY iox_glue IS
 
     | 
  
  
     | 
    
       PORT 
 
     | 
  
  
     | 
    
       ( 
 
     | 
  
  
     | 
    
       	CLK : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	RESET_N : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	ENA : OUT STD_LOGIC;
 
     | 
  
  
     | 
    
       	ADDR : OUT STD_LOGIC_VECTOR(7 downto 1);
 
     | 
  
  
     | 
    
       	RW : OUT STD_LOGIC;
 
     | 
  
  
     | 
    
       	WRITE_DATA : OUT STD_LOGIC_VECTOR(7 downto 0);
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	BUSY : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	READ_DATA : IN STD_LOGIC_VECTOR(7 downto 0);
 
     | 
  
  
     | 
    
       	ERROR : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	INT : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	POT_RESET : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	KEYBOARD_SCAN : IN STD_LOGIC_VECTOR(5 downto 0);
 
     | 
  
  
     | 
    
       	KEYBOARD_RESPONSE : OUT STD_LOGIC_VECTOR(1 downto 0);
 
     | 
  
  
     | 
    
       	KEYBOARD_SCAN_UPDATE : IN STD_LOGIC;
 
     | 
  
  
     | 
    
       	KEYBOARD_SCAN_ENABLE : OUT STD_LOGIC
 
     | 
  
  
     | 
    
       );
 
     | 
  
  
     | 
    
       END iox_glue;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       ARCHITECTURE vhdl OF iox_glue IS
 
     | 
  
  
     | 
    
       	-- requests to send to i2c
 
     | 
  
  
     | 
    
       	signal state_reg : std_logic_vector(3 downto 0);
 
     | 
  
  
     | 
    
       	signal state_next : std_logic_vector(3 downto 0);
 
     | 
  
  
     | 
    
       	constant state_setup1 : std_logic_vector(3 downto 0) := "0000";
 
     | 
  
  
     | 
    
       	constant state_setup2 : std_logic_vector(3 downto 0) := "0001";
 
     | 
  
  
     | 
    
       	constant state_setup3 : std_logic_vector(3 downto 0) := "0010";
 
     | 
  
  
     | 
    
       	constant state_setup4 : std_logic_vector(3 downto 0) := "0011";
 
     | 
  
  
     | 
    
       	constant state_setup5 : std_logic_vector(3 downto 0) := "0100";
 
     | 
  
  
     | 
    
       	constant state_setup6 : std_logic_vector(3 downto 0) := "0101";
 
     | 
  
  
     | 
    
       	--addr,$4f (open drain),00000001 (port 0 is open drain, port 1 is driven)
 
     | 
  
  
     | 
    
       	--addr,$06 (cfg port0), 00000000 
 
     | 
  
  
     | 
    
       	--addr,$07 (cfg port1), 00000011 (p1_0,p1_1 are inputs)
 
     | 
  
  
     | 
    
       	--addr,$47 (pull up/down),00000011 (use pull ups/downs)
 
     | 
  
  
     | 
    
       	--addr,$49 (pull up/down),00000011 (use pull up 100ks)
 
     | 
  
  
     | 
    
       	constant state_kbscan : std_logic_vector(3 downto 0) := "0110";
 
     | 
  
  
     | 
    
       	constant state_kbread : std_logic_vector(3 downto 0) := "0111";
 
     | 
  
  
     | 
    
       	constant state_potreset : std_logic_vector(3 downto 0) := "1000";
 
     | 
  
  
     | 
    
       	constant state_setup7 : std_logic_vector(3 downto 0) := "1001";
 
     | 
  
  
     | 
    
       	-- address, write input port0(02),val
 
     | 
  
  
     | 
    
       	-- address, read input port1(01),0xff(in)
 
     | 
  
  
     | 
    
       	-- address, write input port1(03),val
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	signal w2 : std_logic;
 
     | 
  
  
     | 
    
       	signal write1 : std_logic_vector(7 downto 0);
 
     | 
  
  
     | 
    
       	signal write2 : std_logic_vector(7 downto 0);
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	signal i2c_state_reg : std_logic_vector(1 downto 0);
 
     | 
  
  
     | 
    
       	signal i2c_state_next : std_logic_vector(1 downto 0);
 
     | 
  
  
     | 
    
       	constant i2c_state_part1 : std_logic_vector(1 downto 0) := "00";
 
     | 
  
  
     | 
    
       	constant i2c_state_part2 : std_logic_vector(1 downto 0) := "01";
 
     | 
  
  
     | 
    
       	constant i2c_state_part3 : std_logic_vector(1 downto 0) := "10";
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	signal op_complete : std_logic;
 
     | 
  
  
     | 
    
       	signal busy_reg : std_logic;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	signal pot_next : std_logic;
 
     | 
  
  
     | 
    
       	signal pot_reg : std_logic;
 
     | 
  
  
     | 
    
       begin
 
     | 
  
  
     | 
    
       	process(clk,reset_n)
 
     | 
  
  
     | 
    
       	begin
 
     | 
  
  
     | 
    
       		if (reset_n='0') then
 
     | 
  
  
     | 
    
       			state_reg <= state_setup1;
 
     | 
  
  
     | 
    
       			i2c_state_reg <= i2c_state_part1;
 
     | 
  
  
     | 
    
       			busy_reg <= '0';
 
     | 
  
  
     | 
    
       			pot_reg <= '0';
 
     | 
  
  
     | 
    
       		elsif (clk'event and clk='1') then
 
     | 
  
  
     | 
    
       			state_reg <= state_next;
 
     | 
  
  
     | 
    
       			i2c_state_reg <= i2c_state_next;
 
     | 
  
  
     | 
    
       			busy_reg <= busy;
 
     | 
  
  
     | 
    
       			pot_reg <= pot_next;
 
     | 
  
  
     | 
    
       		end if;
 
     | 
  
  
     | 
    
       	end process;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	process(i2c_state_reg,w2,write1,write2,busy_reg,busy)
 
     | 
  
  
     | 
    
       		variable busy_latched : std_logic;
 
     | 
  
  
     | 
    
       	begin
 
     | 
  
  
     | 
    
       		i2c_state_next <= i2c_state_reg;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       		ena <= '0';
 
     | 
  
  
     | 
    
       		addr <= "0100000"; -- $40
 
     | 
  
  
     | 
    
       		rw <= '1';
 
     | 
  
  
     | 
    
       		write_data <= (others=>'0');
 
     | 
  
  
     | 
    
       		op_complete <= '0';
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       		busy_latched := '0';
 
     | 
  
  
     | 
    
       		if(busy_reg = '0' AND busy = '1') then
 
     | 
  
  
     | 
    
       			busy_latched := '1';
 
     | 
  
  
     | 
    
       		end if;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       		case (i2c_state_reg) is
 
     | 
  
  
     | 
    
       			when i2c_state_part1 =>
 
     | 
  
  
     | 
    
       				ena <= '1';
 
     | 
  
  
     | 
    
       				rw <= '0';
 
     | 
  
  
     | 
    
       				write_data <= write1;
 
     | 
  
  
     | 
    
       				
 
     | 
  
  
     | 
    
       				if (busy_latched='1') then
 
     | 
  
  
     | 
    
       					i2c_state_next <= i2c_state_part2;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when i2c_state_part2 =>
 
     | 
  
  
     | 
    
       				ena <= '1';
 
     | 
  
  
     | 
    
       				rw <= not(w2);
 
     | 
  
  
     | 
    
       				write_data <= write2;
 
     | 
  
  
     | 
    
       				
 
     | 
  
  
     | 
    
       				if (busy_latched='1') then
 
     | 
  
  
     | 
    
       					i2c_state_next <= i2c_state_part3;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when i2c_state_part3 =>
 
     | 
  
  
     | 
    
       				ena <= '0';
 
     | 
  
  
     | 
    
       				if (busy='0') then
 
     | 
  
  
     | 
    
       					i2c_state_next <= i2c_state_part1;
 
     | 
  
  
     | 
    
       					op_complete <= '1';
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when others =>
 
     | 
  
  
     | 
    
       				i2c_state_next <= i2c_state_part1;
 
     | 
  
  
     | 
    
       		end case;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       	end process;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       	process(state_reg,pot_reset,keyboard_scan,busy,busy_reg,read_data,op_complete,pot_reg)
 
     | 
  
  
     | 
    
       	begin
 
     | 
  
  
     | 
    
       		state_next <= state_reg;
 
     | 
  
  
     | 
    
       		pot_next <= pot_reg;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       		keyboard_response <= "11";
 
     | 
  
  
     | 
    
       		keyboard_scan_enable <= '0';
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       		w2 <= '0';
 
     | 
  
  
     | 
    
       		write1 <= x"ff";
 
     | 
  
  
     | 
    
       		write2 <= x"ff";
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       		case (state_reg) is
 
     | 
  
  
     | 
    
       			when state_setup1 =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"4f";
 
     | 
  
  
     | 
    
       				write2 <= "00000001";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_setup2;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_setup2 =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"06";
 
     | 
  
  
     | 
    
       				write2 <= "00000000";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_setup3;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_setup3 =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"07";
 
     | 
  
  
     | 
    
       				write2 <= "00000011";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_setup4;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_setup4 =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"47";
 
     | 
  
  
     | 
    
       				write2 <= "00000011";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_setup5;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_setup5 =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"49";
 
     | 
  
  
     | 
    
       				write2 <= "00000011";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_setup6;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_setup6 =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"4b";
 
     | 
  
  
     | 
    
       				write2 <= "11111100";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_setup7;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_setup7 =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"4f";
 
     | 
  
  
     | 
    
       				write2 <= "00000001";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_kbscan;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_potreset =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"02";
 
     | 
  
  
     | 
    
       				write2 <= not(pot_reset&pot_reset&pot_reset&pot_reset&pot_reset&pot_reset&pot_reset&pot_reset);
 
     | 
  
  
     | 
    
       				pot_next <= pot_reset;
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					state_next <= state_kbscan;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_kbread =>
 
     | 
  
  
     | 
    
       				w2 <= '0';
 
     | 
  
  
     | 
    
       				write1 <= x"01";
 
     | 
  
  
     | 
    
       				write2 <= x"ff";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					keyboard_response <= read_data(0)&read_data(1);
 
     | 
  
  
     | 
    
       					keyboard_scan_enable <= '1';
 
     | 
  
  
     | 
    
       					state_next <= state_kbscan;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when state_kbscan =>
 
     | 
  
  
     | 
    
       				w2 <= '1';
 
     | 
  
  
     | 
    
       				write1 <= x"03";
 
     | 
  
  
     | 
    
       				-- Some pokey bits are inverted (k2,k1,k0,k5), handle FPGA side
 
     | 
  
  
     | 
    
       				--write2 <= not(keyboard_scan(5))&keyboard_scan(4)&keyboard_scan(3)¬(keyboard_scan(0)&keyboard_scan(1)&keyboard_scan(2))&"00";
 
     | 
  
  
     | 
    
       				write2 <= keyboard_scan(5)&keyboard_scan(4)&keyboard_scan(3)&keyboard_scan(0)&keyboard_scan(1)&keyboard_scan(2)&"00";
 
     | 
  
  
     | 
    
       				if (op_complete='1') then
 
     | 
  
  
     | 
    
       					if(not(pot_reset=pot_reg)) then
 
     | 
  
  
     | 
    
       						state_next <= state_potreset;
 
     | 
  
  
     | 
    
       					else
 
     | 
  
  
     | 
    
       						state_next <= state_kbread;
 
     | 
  
  
     | 
    
       					end if;
 
     | 
  
  
     | 
    
       				end if;
 
     | 
  
  
     | 
    
       			when others =>
 
     | 
  
  
     | 
    
       				state_next <= state_setup1;
 
     | 
  
  
     | 
    
       		end case;
 
     | 
  
  
     | 
    
       		
 
     | 
  
  
     | 
    
       	end process;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       end vhdl;
 
     | 
  
  
     | 
    
       
     | 
  
  
     | 
    
       
     |