Description
This is an old post I never finished. I just thought I would at least share the code for the project. I made a keyboard interface with PS2 that plugged into the Spartan 6 Microblaze. You will need to make sure you have the right voltage and resistors in the right place. There is also a wire for the clock and data. PS2 is a pretty old standard and there are a lot of diagrams out there for it.
VHDL
user_logic.vhd
library ieee; use ieee.std_logic_1164.all; use ieee.std_logic_arith.all; use ieee.std_logic_unsigned.all; library proc_common_v3_00_a; use proc_common_v3_00_a.proc_common_pkg.all; -- DO NOT EDIT ABOVE THIS LINE -------------------- --USER libraries added here ------------------------------------------------------------------------------ -- Entity section ------------------------------------------------------------------------------ -- Definition of Generics: -- C_NUM_REG -- Number of software accessible registers -- C_SLV_DWIDTH -- Slave interface data bus width -- -- Definition of Ports: -- Bus2IP_Clk -- Bus to IP clock -- Bus2IP_Resetn -- Bus to IP reset -- Bus2IP_Data -- Bus to IP data bus -- Bus2IP_BE -- Bus to IP byte enables -- Bus2IP_RdCE -- Bus to IP read chip enable -- Bus2IP_WrCE -- Bus to IP write chip enable -- IP2Bus_Data -- IP to Bus data bus -- IP2Bus_RdAck -- IP to Bus read transfer acknowledgement -- IP2Bus_WrAck -- IP to Bus write transfer acknowledgement -- IP2Bus_Error -- IP to Bus error response ------------------------------------------------------------------------------ entity user_logic is generic ( -- ADD USER GENERICS BELOW THIS LINE --------------- --USER generics added here -- ADD USER GENERICS ABOVE THIS LINE --------------- -- DO NOT EDIT BELOW THIS LINE --------------------- -- Bus protocol parameters, do not add to or delete C_NUM_REG : integer := 4; C_SLV_DWIDTH : integer := 32 -- DO NOT EDIT ABOVE THIS LINE --------------------- ); port ( -- ADD USER PORTS BELOW THIS LINE ------------------ --USER ports added here -- clk, rst : in std_logic; ps2d, ps2c : in std_logic; -- rx_en : in std_logic; -- rx_done_tick : out std_logic; shift_en : out std_logic; dout : out std_logic_vector(7 downto 0); myinterrupt : out std_logic; -- ADD USER PORTS ABOVE THIS LINE ------------------ -- DO NOT EDIT BELOW THIS LINE --------------------- -- Bus protocol ports, do not add to or delete Bus2IP_Clk : in std_logic; Bus2IP_Resetn : in std_logic; Bus2IP_Data : in std_logic_vector(C_SLV_DWIDTH-1 downto 0); Bus2IP_BE : in std_logic_vector(C_SLV_DWIDTH/8-1 downto 0); Bus2IP_RdCE : in std_logic_vector(C_NUM_REG-1 downto 0); Bus2IP_WrCE : in std_logic_vector(C_NUM_REG-1 downto 0); IP2Bus_Data : out std_logic_vector(C_SLV_DWIDTH-1 downto 0); IP2Bus_RdAck : out std_logic; IP2Bus_WrAck : out std_logic; IP2Bus_Error : out std_logic -- DO NOT EDIT ABOVE THIS LINE --------------------- ); attribute MAX_FANOUT : string; attribute SIGIS : string; attribute SIGIS of Bus2IP_Clk : signal is "CLK"; attribute SIGIS of Bus2IP_Resetn : signal is "RST"; end entity user_logic; ------------------------------------------------------------------------------ -- Architecture section ------------------------------------------------------------------------------ architecture IMP of user_logic is --USER signal declarations added here, as needed for user logic type statetype is (idle, dps, load); signal state_reg, state_next : statetype; -- filter signal filter_reg, filter_next : std_logic_vector(7 downto 0); signal f_ps2c_reg, f_ps2c_next : std_logic; signal b_reg, b_next : std_logic_vector(10 downto 0); signal n_reg, n_next : unsigned(3 downto 0); signal shift_reg, shift_next : std_logic := '0'; signal fall_edge : std_logic; signal end_buff : std_logic; -- signal rx_en : std_logic; signal zeros : std_logic_vector(C_SLV_DWIDTH-1 downto 0); ------------------------------------------ -- Signals for user logic slave model s/w accessible register example ------------------------------------------ signal slv_reg0 : std_logic_vector(C_SLV_DWIDTH-1 downto 0); signal slv_reg1 : std_logic_vector(C_SLV_DWIDTH-1 downto 0); signal slv_reg2 : std_logic_vector(C_SLV_DWIDTH-1 downto 0); signal slv_reg3 : std_logic_vector(C_SLV_DWIDTH-1 downto 0); signal slv_reg_write_sel : std_logic_vector(3 downto 0); signal slv_reg_read_sel : std_logic_vector(3 downto 0); signal slv_ip2bus_data : std_logic_vector(C_SLV_DWIDTH-1 downto 0); signal slv_read_ack : std_logic; signal slv_write_ack : std_logic; begin --USER logic implementation added here -- filter process (Bus2IP_Clk, Bus2IP_Resetn) begin if (Bus2IP_Resetn = '0') then filter_reg <= (others => '0'); f_ps2c_reg <= '0'; elsif (Bus2IP_Clk'event and Bus2IP_Clk='1') then filter_reg <= filter_next; f_ps2c_reg <= f_ps2c_next; end if; end process; filter_next <= ps2c & filter_reg(7 downto 1); f_ps2c_next <= '1' when filter_reg = "11111111" else '0' when filter_reg = "00000000" else f_ps2c_reg; fall_edge <= f_ps2c_reg and (not f_ps2c_next); -- rx_en will need to be set to 1 always by this design --rx_en <= '1'; zeros <= (others => '0'); -- registers process (Bus2IP_Clk, Bus2IP_Resetn) begin if (Bus2IP_Resetn = '0') then state_reg <= idle; n_reg <= (others => '0'); b_reg <= (others => '0'); slv_reg0 <= (others => '0'); elsif (Bus2IP_Clk'event and Bus2IP_Clk='1') then state_reg <= state_next; n_reg <= n_next; b_reg <= b_next; shift_reg <= shift_reg; slv_reg0 <= zeros or b_reg(8 downto 1); end if; end process; -- next-state logic process(state_reg, n_reg, b_reg, fall_edge, ps2d) begin myinterrupt <= '0'; --rx_done_tick <= '0'; state_next <= state_reg; n_next <= n_reg; b_next <= b_reg; case state_reg is when idle => -- myinterrupt <= '1'; if (fall_edge = '1') then --shift in start bit b_next <= ps2d & b_reg(10 downto 1); n_next <= "1001"; -- set count to 8 again state_next <= dps; end if; when dps => -- myinterrupt <= '0'; if (fall_edge = '1' ) then b_next <= ps2d & b_reg(10 downto 1); if (n_reg = 0) then state_next <= load; else n_next <= n_reg - 1; end if; end if; when load => -- here we handle if signal f0 and following signal are -- asserted - we don't want to transmit them to dout. -- one more state to complete last shift state_next <= idle; -- rx_done_tick <= '1'; myinterrupt <= '1'; if (b_reg(8 downto 0) = x"12" or b_reg(8 downto 0) = x"59") then shift_next <= not shift_reg; if (shift_reg = '1') then -- rx_done_tick <= '0'; myinterrupt <= '0'; end if; elsif (b_reg(8 downto 0) = "11110000") then end_buff <= '1'; -- rx_done_tick <= '0'; myinterrupt <= '0'; elsif (end_buff = '1') then end_buff <= '0'; -- rx_done_tick <= '0'; myinterrupt <= '0'; end if; end case; end process; shift_en <= shift_reg; dout <= b_reg(8 downto 1); slv_reg_write_sel <= Bus2IP_WrCE(3 downto 0); slv_reg_read_sel <= Bus2IP_RdCE(3 downto 0); slv_write_ack <= Bus2IP_WrCE(0) or Bus2IP_WrCE(1) or Bus2IP_WrCE(2) or Bus2IP_WrCE(3); slv_read_ack <= Bus2IP_RdCE(0) or Bus2IP_RdCE(1) or Bus2IP_RdCE(2) or Bus2IP_RdCE(3); -- implement slave model software accessible register(s) read mux SLAVE_REG_READ_PROC : process( slv_reg_read_sel, slv_reg0, slv_reg1, slv_reg2, slv_reg3 ) is begin case slv_reg_read_sel is when "1000" => slv_ip2bus_data <= slv_reg0; when "0100" => slv_ip2bus_data <= slv_reg1; when "0010" => slv_ip2bus_data <= slv_reg2; when "0001" => slv_ip2bus_data <= slv_reg3; when others => slv_ip2bus_data <= (others => '0'); end case; end process SLAVE_REG_READ_PROC; -- -- ------------------------------------------ -- -- Example code to drive IP to Bus signals -- ------------------------------------------ IP2Bus_Data <= slv_ip2bus_data when slv_read_ack = '1' else (others => '0'); IP2Bus_WrAck <= slv_write_ack; IP2Bus_RdAck <= slv_read_ack; IP2Bus_Error <= '0'; end IMP;
ps2_keyboard.vhd
port ( -- ADD USER PORTS BELOW THIS LINE ------------------ --USER ports added here ps2d, ps2c : in std_logic; myinterrupt : out std_logic; -- ADD USER PORTS ABOVE THIS LINE ------------------
UCF and MPD files
The MPD file needs to be modified first. This will then allow us to add the io ports when mapping the device.
Microblaze Interrupt Handler
microblaze_register_handler(interrupt_handler_dispatcher, NULL); XIntc_EnableIntr(XPAR_INTC_0_BASEADDR, (XPAR_FIT_TIMER_0_INTERRUPT_MASK | XPAR_PUSH_BUTTONS_5BITS_IP2INTC_IRPT_MASK | XPAR_PS2_KEYBOARD_0_MYINTERRUPT_MASK | XPAR_AXI_AC97_0_INTERRUPT_MASK )); XIntc_MasterEnable(XPAR_INTC_0_BASEADDR); microblaze_enable_interrupts();
Next
void interrupt_handler_dispatcher(void* ptr) { int intc_status = XIntc_GetIntrStatus(XPAR_INTC_0_BASEADDR); // Check the AC97. Just one method - no need for a separate handler if (intc_status & XPAR_AXI_AC97_0_INTERRUPT_MASK) { XIntc_AckIntr(XPAR_INTC_0_BASEADDR, XPAR_AXI_AC97_0_INTERRUPT_MASK); fillSound(); } // Check the FIT interrupt first. if (intc_status & XPAR_FIT_TIMER_0_INTERRUPT_MASK){ XIntc_AckIntr(XPAR_INTC_0_BASEADDR, XPAR_FIT_TIMER_0_INTERRUPT_MASK); timer_interrupt_handler(); } // Check the push buttons. if (intc_status & XPAR_PUSH_BUTTONS_5BITS_IP2INTC_IRPT_MASK){ XIntc_AckIntr(XPAR_INTC_0_BASEADDR, XPAR_PUSH_BUTTONS_5BITS_IP2INTC_IRPT_MASK); pb_interrupt_handler(); } if (intc_status & XPAR_PS2_KEYBOARD_0_MYINTERRUPT_MASK) { XIntc_AckIntr(XPAR_INTC_0_BASEADDR, XPAR_PS2_KEYBOARD_0_MYINTERRUPT_MASK); ps2_interrupt_handler(); } }
PS2 Interrupt Handler
void ps2_interrupt_handler() { int buttonValue = XGpio_ReadReg(XPAR_PS2_KEYBOARD_0_BASEADDR, 0x0); xil_printf("rnValue0=%d",buttonValue); if (getGameInAction() == 0) { switch (buttonValue) { // left button case 28: setTankPositionGlobal(getTankPositionGlobal() - 5); drawTank(framePointer0); break; case 27: if (!isHaveTankBullet()) { setHaveTankBullet(1); setHaveTankBulletSound(1); setTankBulletPositionX(getTankPositionGlobal() + 15); setTankBulletPositionY(TANK_Y_POSITION - 2*TANK_BULLET_HEIGHT); drawTankBullet(framePointer0); } break; case 35: setTankPositionGlobal(getTankPositionGlobal() + 5); drawTank(framePointer0); break; } } }