|
|
|
-- GPIO module for microwatt
|
|
|
|
library ieee;
|
|
|
|
use ieee.std_logic_1164.all;
|
|
|
|
use ieee.numeric_std.all;
|
|
|
|
|
|
|
|
library work;
|
|
|
|
use work.wishbone_types.all;
|
|
|
|
|
|
|
|
entity gpio is
|
|
|
|
generic (
|
|
|
|
NGPIO : integer := 32
|
|
|
|
);
|
|
|
|
port (
|
|
|
|
clk : in std_ulogic;
|
|
|
|
rst : in std_ulogic;
|
|
|
|
|
|
|
|
-- Wishbone
|
|
|
|
wb_in : in wb_io_master_out;
|
|
|
|
wb_out : out wb_io_slave_out;
|
|
|
|
|
|
|
|
-- GPIO lines
|
|
|
|
gpio_in : in std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
gpio_out : out std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
-- 1 = output, 0 = input
|
|
|
|
gpio_dir : out std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
|
|
|
|
-- Interrupt
|
|
|
|
intr : out std_ulogic
|
|
|
|
);
|
|
|
|
end entity gpio;
|
|
|
|
|
|
|
|
architecture behaviour of gpio is
|
|
|
|
constant GPIO_REG_BITS : positive := 5;
|
|
|
|
|
|
|
|
-- Register addresses, matching addr downto 2, so 4 bytes per reg
|
|
|
|
constant GPIO_REG_DATA_OUT : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00000";
|
|
|
|
constant GPIO_REG_DATA_IN : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00001";
|
|
|
|
constant GPIO_REG_DIR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00010";
|
|
|
|
constant GPIO_REG_DATA_SET : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00100";
|
|
|
|
constant GPIO_REG_DATA_CLR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "00101";
|
|
|
|
|
|
|
|
constant GPIO_REG_INT_EN : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01000";
|
|
|
|
constant GPIO_REG_INT_STAT : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01001";
|
|
|
|
-- write 1 to clear
|
|
|
|
constant GPIO_REG_INT_CLR : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01100";
|
|
|
|
-- edge 0, level 1
|
|
|
|
constant GPIO_REG_INT_TYPE : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01101";
|
|
|
|
-- for edge: trigger on either edge = 1
|
|
|
|
constant GPIO_REG_INT_BOTH_EDGE : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01110";
|
|
|
|
-- for edge: rising 0, falling 1
|
|
|
|
-- for level: high 0, low 1
|
|
|
|
constant GPIO_REG_INT_LEVEL : std_ulogic_vector(GPIO_REG_BITS-1 downto 0) := "01111";
|
|
|
|
|
|
|
|
-- Current output value and direction
|
|
|
|
signal reg_data : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_dirn : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_in0 : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_in1 : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_in2 : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
|
|
|
|
signal reg_intr_en : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_intr_hit : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_intr_type : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_intr_level : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
signal reg_intr_both : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
|
|
|
|
signal wb_rsp : wb_io_slave_out;
|
|
|
|
signal reg_out : std_ulogic_vector(NGPIO - 1 downto 0);
|
|
|
|
|
|
|
|
constant ZEROS : std_ulogic_vector(NGPIO-1 downto 0) := (others => '0');
|
|
|
|
begin
|
|
|
|
intr <= '0' when reg_intr_hit = ZEROS else '1';
|
|
|
|
gpio_out <= reg_data;
|
|
|
|
gpio_dir <= reg_dirn;
|
|
|
|
|
|
|
|
-- Wishbone response
|
|
|
|
wb_rsp.ack <= wb_in.cyc and wb_in.stb;
|
|
|
|
with wb_in.adr(GPIO_REG_BITS - 1 downto 0) select reg_out <=
|
|
|
|
reg_data when GPIO_REG_DATA_OUT,
|
|
|
|
reg_in1 when GPIO_REG_DATA_IN,
|
|
|
|
reg_dirn when GPIO_REG_DIR,
|
|
|
|
reg_intr_en when GPIO_REG_INT_EN,
|
|
|
|
reg_intr_hit when GPIO_REG_INT_STAT,
|
|
|
|
reg_intr_type when GPIO_REG_INT_TYPE,
|
|
|
|
reg_intr_both when GPIO_REG_INT_BOTH_EDGE,
|
|
|
|
reg_intr_level when GPIO_REG_INT_LEVEL,
|
|
|
|
(others => '0') when others;
|
|
|
|
wb_rsp.dat(wb_rsp.dat'left downto NGPIO) <= (others => '0');
|
|
|
|
wb_rsp.dat(NGPIO - 1 downto 0) <= reg_out;
|
|
|
|
wb_rsp.stall <= '0';
|
|
|
|
|
|
|
|
regs_rw: process(clk)
|
|
|
|
variable trig : std_logic_vector(0 to 2);
|
|
|
|
variable change : std_logic;
|
|
|
|
variable intr_hit : boolean;
|
|
|
|
begin
|
|
|
|
if rising_edge(clk) then
|
|
|
|
wb_out <= wb_rsp;
|
|
|
|
for i in NGPIO - 1 downto 0 loop
|
|
|
|
-- interrupt triggers. reg_in1 is current value
|
|
|
|
if reg_intr_type(i) = '0' then
|
|
|
|
-- edge
|
|
|
|
change := '0' when (reg_in1(i) = reg_in2(i)) else '1';
|
|
|
|
trig := change & reg_intr_both(i) & reg_intr_level(i);
|
|
|
|
case trig is
|
|
|
|
-- both
|
|
|
|
when "110" | "111" => intr_hit := true;
|
|
|
|
-- rising
|
|
|
|
when "100" => intr_hit := reg_in1(i) = '1';
|
|
|
|
-- falling
|
|
|
|
when "101" => intr_hit := reg_in1(i) = '0';
|
|
|
|
when others => intr_hit := false;
|
|
|
|
end case;
|
|
|
|
else
|
|
|
|
-- level
|
|
|
|
intr_hit := reg_in1(i) = not reg_intr_level(i);
|
|
|
|
end if;
|
|
|
|
reg_intr_hit(i) <= '1' when intr_hit and reg_intr_en(i) = '1';
|
|
|
|
|
|
|
|
end loop;
|
|
|
|
|
|
|
|
-- previous value for interrupt edge detection
|
|
|
|
reg_in2 <= reg_in1;
|
|
|
|
-- 2 flip flops to cross from async input to sys clock domain
|
|
|
|
reg_in1 <= reg_in0;
|
|
|
|
reg_in0 <= gpio_in;
|
|
|
|
|
|
|
|
if rst = '1' then
|
|
|
|
reg_data <= (others => '0');
|
|
|
|
reg_dirn <= (others => '0');
|
|
|
|
reg_intr_en <= (others => '0');
|
|
|
|
reg_intr_hit <= (others => '0');
|
|
|
|
reg_intr_type <= (others => '0');
|
|
|
|
reg_intr_both <= (others => '0');
|
|
|
|
reg_intr_level <= (others => '0');
|
|
|
|
wb_out.ack <= '0';
|
|
|
|
else
|
|
|
|
if wb_in.cyc = '1' and wb_in.stb = '1' and wb_in.we = '1' then
|
|
|
|
case wb_in.adr(GPIO_REG_BITS - 1 downto 0) is
|
|
|
|
when GPIO_REG_DATA_OUT =>
|
|
|
|
reg_data <= wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_DIR =>
|
|
|
|
reg_dirn <= wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_DATA_SET =>
|
|
|
|
reg_data <= reg_data or wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_DATA_CLR =>
|
|
|
|
reg_data <= reg_data and not wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_INT_EN =>
|
|
|
|
reg_intr_en <= wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_INT_CLR =>
|
|
|
|
reg_intr_hit <= reg_intr_hit and not wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_INT_TYPE =>
|
|
|
|
reg_intr_type <= wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_INT_BOTH_EDGE =>
|
|
|
|
reg_intr_both <= wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when GPIO_REG_INT_LEVEL =>
|
|
|
|
reg_intr_level <= wb_in.dat(NGPIO - 1 downto 0);
|
|
|
|
when others =>
|
|
|
|
end case;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
end process;
|
|
|
|
|
|
|
|
end architecture behaviour;
|
|
|
|
|