You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
microwatt/fpga/pp_soc_gpio.vhdl

200 lines
7.7 KiB
VHDL

-- Sim GPIO, based on potato GPIO by
-- Kristian Klomsten Skordal.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.sim_console.all;
--! @brief Generic Wishbone GPIO Module.
--!
--! The following registers are defined:
--! |---------|------------------------------------------------------------------|
--! | Address | Description |
--! |---------|------------------------------------------------------------------|
--! | 0x00 | Describes the port: (read-only) |
--! | | bits 0-5: The number of pins |
--! | 0x08 | Input values, one bit per pin (read/write), |
--! | | If the pin is set to output, writing 1 will toggle the pin |
--! | 0x10 | Output values, one bit per pin (read/write) |
--! | 0x18 | Set register, Output = Output | set register (write-only) |
--! | 0x20 | Clear register, Output = Output & ~(clear register) (write-only) |
--! | 0x28 | Type, 4 bits per pin (read/write), pins 0-15 |
--! | 0x30 | Type, 4 bits per pin (read/write), pins 16-31 |
--! | 0x38 | Type, 4 bits per pin (read/write), pins 32-47 |
--! | 0x40 | Type, 4 bits per pin (read/write), pins 48-63 |
--! | | Types: MSB LSB |
--! | | 0 0 0 0 Input, Interrupt disabled |
--! | | 0 0 1 0 Input, Interrupt when low |
--! | | 0 0 1 1 Input, Interrupt when high |
--! | | 0 1 0 0 Input, Interrupt when falling |
--! | | 0 1 0 1 Input, Interrupt when rising |
--! | | 0 1 1 1 Output |
--! | | others Undefined |
--! | 0x48 | Interrupt Triggered, one bit per pin (read/write) |
--! |---------|------------------------------------------------------------------|
--!
--! Writes to the output register for input pins are ignored.
entity pp_soc_gpio is
generic(
NUM_GPIOS : natural := 64
);
port(
clk : in std_logic;
reset : in std_logic;
irq : out std_logic;
-- GPIO interface:
gpio : inout std_logic_vector(NUM_GPIOS - 1 downto 0);
-- Wishbone interface:
wb_adr_in : in std_logic_vector(7 downto 0);
wb_dat_in : in std_logic_vector(63 downto 0);
wb_dat_out : out std_logic_vector(63 downto 0);
wb_cyc_in : in std_logic;
wb_stb_in : in std_logic;
wb_we_in : in std_logic;
wb_ack_out : out std_logic
);
end entity pp_soc_gpio;
architecture behaviour of pp_soc_gpio is
type type_array is array (natural range 0 to NUM_GPIOS - 1) of
std_logic_vector(3 downto 0);
signal input_buffer : std_logic_vector(NUM_GPIOS - 1 downto 0);
signal input_register_prev : std_logic_vector(NUM_GPIOS - 1 downto 0);
signal input_register : std_logic_vector(NUM_GPIOS - 1 downto 0);
signal output_register : std_logic_vector(NUM_GPIOS - 1 downto 0);
signal type_register : type_array;
signal irq_triggered_register : std_logic_vector(NUM_GPIOS - 1 downto 0);
signal ack : std_logic := '0';
begin
assert NUM_GPIOS > 0 and NUM_GPIOS <= 64
report "Only a number between 1 and 64 (inclusive) GPIOs are supported!"
severity FAILURE;
wb_ack_out <= ack and wb_cyc_in and wb_stb_in;
wishbone: process(clk)
begin
if rising_edge(clk) then
for i in 0 to NUM_GPIOS - 1 loop
gpio(i) <= output_register(i) when type_register(i) = b"0111" else 'Z';
input_register_prev(i) <= input_register(i);
input_register(i) <= input_buffer(i);
input_buffer(i) <= gpio(i) when type_register(i) /= b"0111";
case type_register(i) is
when b"0010" =>
if gpio(i) = '0' then
irq_triggered_register(i) <= '1';
irq <= '1';
end if;
when b"0011" =>
if gpio(i) = '1' then
irq_triggered_register(i) <= '1';
irq <= '1';
end if;
when b"0100" =>
if input_register(i) = '0' and input_register_prev(i) = '1' then
irq_triggered_register(i) <= '1';
irq <= '1';
end if;
when b"0101" =>
if input_register(i) = '1' and input_register_prev(i) = '0' then
irq_triggered_register(i) <= '1';
irq <= '1';
end if;
when others =>
end case;
end loop;
if reset = '1' then
input_register <= (others => '0');
output_register <= (others => '0');
wb_dat_out <= (others => '0');
for i in 0 to NUM_GPIOS - 1 loop
type_register(i) <= (others => '0');
end loop;
irq_triggered_register <= (others => '0');
ack <= '0';
else
if wb_cyc_in = '1' and wb_stb_in = '1' and ack = '0' then
if wb_we_in = '1' then
case wb_adr_in is
when x"08" => --! Input Value
output_register <= output_register xor wb_dat_in(NUM_GPIOS - 1 downto 0);
when x"10" => --! Output Value
output_register <= wb_dat_in(NUM_GPIOS - 1 downto 0);
when x"18" => --! Set
output_register <= output_register OR wb_dat_in(NUM_GPIOS - 1 downto 0);
when x"20" => --! Clear
output_register <= output_register AND NOT(wb_dat_in(NUM_GPIOS - 1 downto 0));
when x"28" => --! Type Pins 0-15
for i in 0 to MINIMUM(NUM_GPIOS - 1, 15) loop
type_register(i) <= (wb_dat_in((i + 1) * 4 - 1 downto i * 4));
end loop;
when x"30" => --! Type Pins 16-31
for i in 16 to MINIMUM(NUM_GPIOS - 1, 31) loop
type_register(i) <= (wb_dat_in((i - 16 + 1) * 4 - 1 downto (i - 16) * 4));
end loop;
when x"38" => --! Type Pins 32-47
for i in 32 to MINIMUM(NUM_GPIOS - 1, 47) loop
type_register(i) <= (wb_dat_in((i - 32 + 1) * 4 - 1 downto (i - 32) * 4));
end loop;
when x"40" => --! Type Pins 48-63
for i in 48 to MINIMUM(NUM_GPIOS - 1, 63) loop
type_register(i) <= (wb_dat_in((i - 48 + 1) * 4 - 1 downto (i - 48) * 4));
end loop;
when others =>
end case;
ack <= '1';
else
case wb_adr_in is
when x"00" => --! Description
wb_dat_out <= std_logic_vector(to_unsigned(NUM_GPIOS, wb_dat_out'length));
when x"08" => --! Input Value
wb_dat_out <= std_logic_vector(resize(unsigned(input_register), wb_dat_out'length));
when x"10" => --! Output value
wb_dat_out <= std_logic_vector(resize(unsigned(output_register), wb_dat_out'length));
when x"28" => --! Type Pins 0-15
for i in 0 to MINIMUM(NUM_GPIOS - 1, 15) loop
wb_dat_out((i + 1) * 4 - 1 downto i * 4) <= type_register(i);
end loop;
when x"30" => --! Type Pins 16-31
for i in 21 to MINIMUM(NUM_GPIOS - 1, 31) loop
wb_dat_out((i - 16 + 1) * 4 - 1 downto (i - 16) * 4) <= type_register(i);
end loop;
when x"38" => --! Type Pins 32-47
for i in 32 to MINIMUM(NUM_GPIOS - 1, 47) loop
wb_dat_out((i - 32 + 1) * 4 - 1 downto (i - 32) * 4) <= type_register(i);
end loop;
when x"40" => --! Type Pins 32-47
for i in 32 to MINIMUM(NUM_GPIOS - 1, 47) loop
wb_dat_out((i - 32 + 1) * 4 - 1 downto (i - 32) * 4) <= type_register(i);
end loop;
when x"48" => --! Interrupts triggered
wb_dat_out <= std_logic_vector(resize(unsigned(irq_triggered_register), wb_dat_out'length));
irq_triggered_register <= (others => '0');
irq <= '0';
when others =>
end case;
report "ack";
ack <= '1';
end if;
elsif wb_stb_in = '0' then
ack <= '0';
end if;
end if;
end if;
end process wishbone;
end architecture behaviour;