repo2/atari_chips/pokeyv2/iox_gluesid.vhdl @ 1462
| 1241 | markw | ---------------------------------------------------------------------------
 | |
| -- (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 PCAL6408A 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
 | |||
| 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;
 | |||
| 	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
 | |||
| 1283 | markw | 	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_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";
 | |||
| 1241 | markw | 	--addr,$4f (driven),00000000 (port 1 is driven)
 | |
| 	--addr,$03 (cfg port1), 10000001 (p1_0,p1_7 are inputs)
 | |||
| 	--addr,$43 (pull up/down),10000001 (use pull ups/downs)
 | |||
| 	--addr,$44 (pull up/down),10000001 (use pull up 100ks)
 | |||
| 1283 | markw | 	constant state_kbscan : std_logic_vector(3 downto 0) := "0110";
 | |
| 	constant state_kbread : std_logic_vector(3 downto 0) := "0111";
 | |||
| 	constant state_setup7 : std_logic_vector(3 downto 0) := "1001";
 | |||
| 	constant state_kbscan_delay : std_logic_vector(3 downto 0) := "1010";
 | |||
| 1241 | markw | 	-- 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);
 | |||
| 1283 | markw | 	signal i2c_state_reg : std_logic_vector(1 downto 0);
 | |
| 	signal i2c_state_next : std_logic_vector(1 downto 0);
 | |||
| 	constant i2c_state_idle : std_logic_vector(1 downto 0) := "00";
 | |||
| 	constant i2c_state_part1 : std_logic_vector(1 downto 0) := "01";
 | |||
| 	constant i2c_state_part2 : std_logic_vector(1 downto 0) := "10";
 | |||
| 	constant i2c_state_part3 : std_logic_vector(1 downto 0) := "11";
 | |||
| 1241 | markw | ||
| 	signal op_complete : std_logic;
 | |||
| 	signal busy_reg : std_logic;
 | |||
| 	signal keyboard_response_reg : std_logic_vector(1 downto 0);
 | |||
| 	signal keyboard_response_next : std_logic_vector(1 downto 0);
 | |||
| 	signal keyboard_scan_update_pending_reg : std_logic;
 | |||
| 	signal keyboard_scan_update_pending_next : std_logic;
 | |||
| 	signal request : std_logic;
 | |||
| begin
 | |||
| 	process(clk,reset_n)
 | |||
| 	begin
 | |||
| 		if (reset_n='0') then
 | |||
| 			state_reg <= state_setup1;
 | |||
| 			i2c_state_reg <= i2c_state_idle;
 | |||
| 			busy_reg <= '0';
 | |||
| 			keyboard_response_reg <= "11";
 | |||
| 			keyboard_scan_update_pending_reg <= '0';
 | |||
| 		elsif (clk'event and clk='1') then
 | |||
| 			state_reg <= state_next;
 | |||
| 			i2c_state_reg <= i2c_state_next;
 | |||
| 			busy_reg <= busy;
 | |||
| 			keyboard_response_reg <= keyboard_response_next;
 | |||
| 			keyboard_scan_update_pending_reg <= keyboard_scan_update_pending_next;
 | |||
| 		end if;
 | |||
| 	end process;
 | |||
| 	process(i2c_state_reg,w2,write1,write2,busy_reg,busy,request)
 | |||
| 		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_idle =>
 | |||
| 				if (request = '1') then
 | |||
| 					i2c_state_next <= i2c_state_part1;
 | |||
| 				end if;
 | |||
| 			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_idle;
 | |||
| 					op_complete <= '1';
 | |||
| 				end if;
 | |||
| 			when others =>
 | |||
| 				i2c_state_next <= i2c_state_idle;
 | |||
| 		end case;
 | |||
| 	end process;
 | |||
| 	process(state_reg,keyboard_scan,keyboard_scan_update,keyboard_scan_update_pending_reg,busy,busy_reg,read_data,op_complete,int,keyboard_response_reg)
 | |||
| 	begin
 | |||
| 		state_next <= state_reg;
 | |||
| 		keyboard_scan_enable <= '0';
 | |||
| 		keyboard_response_next <= keyboard_response_reg;
 | |||
| 		keyboard_scan_update_pending_next <= keyboard_scan_update_pending_reg or keyboard_scan_update;
 | |||
| 		request <= '0';
 | |||
| 		w2 <= '0';
 | |||
| 		write1 <= x"ff";
 | |||
| 		write2 <= x"ff";
 | |||
| 		case (state_reg) is
 | |||
| 			when state_setup1 =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '1';
 | |||
| 				write1 <= x"4f";
 | |||
| 				write2 <= "00000000";
 | |||
| 				if (op_complete='1') then
 | |||
| 					state_next <= state_setup3;
 | |||
| 				end if;
 | |||
| 			when state_setup3 =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '1';
 | |||
| 				write1 <= x"03";
 | |||
| 				write2 <= "10000001";
 | |||
| 				if (op_complete='1') then
 | |||
| 					state_next <= state_setup4;
 | |||
| 				end if;
 | |||
| 			when state_setup4 =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '1';
 | |||
| 				write1 <= x"43";
 | |||
| 				write2 <= "10000001";
 | |||
| 				if (op_complete='1') then
 | |||
| 					state_next <= state_setup5;
 | |||
| 				end if;
 | |||
| 			when state_setup5 =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '1';
 | |||
| 				write1 <= x"44";
 | |||
| 				write2 <= "10000001";
 | |||
| 				if (op_complete='1') then
 | |||
| 					state_next <= state_setup6;
 | |||
| 				end if;
 | |||
| 			when state_setup6 =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '1';
 | |||
| 				write1 <= x"45";
 | |||
| 				write2 <= "01111110";
 | |||
| 				if (op_complete='1') then
 | |||
| 					state_next <= state_setup7;
 | |||
| 				end if;
 | |||
| 			when state_setup7 =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '1';
 | |||
| 				write1 <= x"4f";
 | |||
| 				write2 <= "00000000";
 | |||
| 				if (op_complete='1') then
 | |||
| 					state_next <= state_kbscan;
 | |||
| 				end if;
 | |||
| 			when state_kbread =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '0';
 | |||
| 				write1 <= x"00";
 | |||
| 				write2 <= x"ff";
 | |||
| 				if (op_complete='1') then
 | |||
| 					keyboard_response_next <= read_data(0)&read_data(7);
 | |||
| 					state_next <= state_kbscan_delay;
 | |||
| 				end if;
 | |||
| 			when state_kbscan_delay =>
 | |||
| 				if (int='0') then
 | |||
| 					state_next <= state_kbread;
 | |||
| 				end if;
 | |||
| 				if (keyboard_scan_update_pending_reg='1') then
 | |||
| 				   keyboard_scan_update_pending_next <= '0';
 | |||
| 					keyboard_scan_enable <= '1';
 | |||
| 					state_next <= state_kbscan;
 | |||
| 				end if;
 | |||
| 			--	keyboard_scan_enable <= '1';
 | |||
| 			--	state_next <= state_kbscan;
 | |||
| 			when state_kbscan =>
 | |||
| 				request <= '1';
 | |||
| 				w2 <= '1';
 | |||
| 				write1 <= x"01";
 | |||
| 				-- 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 <= "0"&keyboard_scan(0)&keyboard_scan(1)&keyboard_scan(2)&keyboard_scan(3)&keyboard_scan(4)&keyboard_scan(5)&"0";
 | |||
| --v1
 | |||
| --p1_0 - KR2 (act low)
 | |||
| --p1_1 - KR1 (act low)
 | |||
| --p1_2 - K2 (act low)
 | |||
| --p1_3 - k1 (act low)
 | |||
| --p1_4 - k0 (act low)
 | |||
| --p1_5 - k3
 | |||
| --p1_6 - k4
 | |||
| --p1_7 - k5 (act low)
 | |||
| --v2
 | |||
| --P0_0,KR2,
 | |||
| --P0_1,K5,
 | |||
| --P0_2,K4
 | |||
| --P0_3,K3
 | |||
| --P0_4,K2
 | |||
| --P0_5,K1
 | |||
| --P0_6,K0
 | |||
| --P0_7,KR1
 | |||
| 				if (op_complete='1') then
 | |||
| 					state_next <= state_kbscan_delay;
 | |||
| 				end if;
 | |||
| 			when others =>
 | |||
| 				state_next <= state_setup1;
 | |||
| 		end case;
 | |||
| 	end process;
 | |||
| 	keyboard_response <= keyboard_response_reg;
 | |||
| end vhdl;
 | |||