Add a GPIO controller and use it to drive the shield I/O pins on the Arty
authorPaul Mackerras <paulus@ozlabs.org>
Tue, 23 Feb 2021 10:44:04 +0000 (21:44 +1100)
committerPaul Mackerras <paulus@ozlabs.org>
Wed, 24 Feb 2021 01:46:01 +0000 (12:46 +1100)
This adds a GPIO controller which provides 32 bits of I/O.  The
registers are modelled on the set used by the gpio-ftgpio010.c driver
in the Linux kernel.  Currently there is no interrupt capability
implemented, though an interrupt line from the GPIO subsystem to the
XICS has been connected.

For the Arty A7 board, GPIO lines 0 to 13 are connected to the pins
labelled IO0 to IO13 on the "shield" connector, GPIO lines 14 to 29
connect to IO26 to IO41, GPIO line 30 connects to the pin labelled A
(aka IO42), and GPIO line 31 is connected to LED 7.

Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Makefile
fpga/arty_a7.xdc
fpga/top-arty.vhdl
gpio.vhdl [new file with mode: 0644]
microwatt.core
soc.vhdl

index 678bbfa6ce80ad9ab85d75c6471309b934351bf1..05eff4a7291101cce715c58899896cd8ff2e0044 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -51,7 +51,7 @@ core_files = decode_types.vhdl common.vhdl wishbone_types.vhdl fetch1.vhdl \
        core.vhdl fpu.vhdl
 
 soc_files = $(core_files) wishbone_arbiter.vhdl wishbone_bram_wrapper.vhdl sync_fifo.vhdl \
-       wishbone_debug_master.vhdl xics.vhdl syscon.vhdl soc.vhdl \
+       wishbone_debug_master.vhdl xics.vhdl syscon.vhdl gpio.vhdl soc.vhdl \
        spi_rxtx.vhdl spi_flash_ctrl.vhdl
 
 uart_files = $(wildcard uart16550/*.v)
index 7cdad8dc59b962f293f14fc0303ffe5d0d127484..81c7cf921c29d7b030130caf4e6c9b1eaf8a31de 100644 (file)
@@ -115,39 +115,39 @@ set_property IOB true [get_cells -hierarchical -filter {NAME =~*/spi_rxtx/input_
 # Arduino/chipKIT shield connector
 ################################################################################
 
-#set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { shield_io0 }];
-#set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports { shield_io1 }];
-#set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { shield_io2 }];
-#set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { shield_io3 }];
-#set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports { shield_io4 }];
-#set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { shield_io5 }];
-#set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { shield_io6 }];
-#set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { shield_io7 }];
-#set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { shield_io8 }];
-#set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { shield_io9 }];
-#set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { shield_io10 }];
-#set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { shield_io11 }];
-#set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { shield_io12 }];
-#set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports { shield_io13 }];
-#set_property -dict { PACKAGE_PIN U11 IOSTANDARD LVCMOS33 } [get_ports { shield_io26 }];
-#set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { shield_io27 }];
-#set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports { shield_io28 }];
-#set_property -dict { PACKAGE_PIN R10 IOSTANDARD LVCMOS33 } [get_ports { shield_io29 }];
-#set_property -dict { PACKAGE_PIN R11 IOSTANDARD LVCMOS33 } [get_ports { shield_io30 }];
-#set_property -dict { PACKAGE_PIN R13 IOSTANDARD LVCMOS33 } [get_ports { shield_io31 }];
-#set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 } [get_ports { shield_io32 }];
-#set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { shield_io33 }];
-#set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { shield_io34 }];
-#set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { shield_io35 }];
-#set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 } [get_ports { shield_io36 }];
-#set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { shield_io37 }];
-#set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports { shield_io38 }];
-#set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { shield_io39 }];
-#set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { shield_io40 }];
-#set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { shield_io41 }];
-#set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 } [get_ports { shield_ioa }];
-#set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 } [get_ports { shield_scl }];
-#set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { shield_sda }];
+set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS33 } [get_ports { shield_io[0] }];
+set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS33 } [get_ports { shield_io[1] }];
+set_property -dict { PACKAGE_PIN P14 IOSTANDARD LVCMOS33 } [get_ports { shield_io[2] }];
+set_property -dict { PACKAGE_PIN T11 IOSTANDARD LVCMOS33 } [get_ports { shield_io[3] }];
+set_property -dict { PACKAGE_PIN R12 IOSTANDARD LVCMOS33 } [get_ports { shield_io[4] }];
+set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS33 } [get_ports { shield_io[5] }];
+set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS33 } [get_ports { shield_io[6] }];
+set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS33 } [get_ports { shield_io[7] }];
+set_property -dict { PACKAGE_PIN N15 IOSTANDARD LVCMOS33 } [get_ports { shield_io[8] }];
+set_property -dict { PACKAGE_PIN M16 IOSTANDARD LVCMOS33 } [get_ports { shield_io[9] }];
+set_property -dict { PACKAGE_PIN V17 IOSTANDARD LVCMOS33 } [get_ports { shield_io[10] }];
+set_property -dict { PACKAGE_PIN U18 IOSTANDARD LVCMOS33 } [get_ports { shield_io[11] }];
+set_property -dict { PACKAGE_PIN R17 IOSTANDARD LVCMOS33 } [get_ports { shield_io[12] }];
+set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports { shield_io[13] }];
+set_property -dict { PACKAGE_PIN U11 IOSTANDARD LVCMOS33 } [get_ports { shield_io[26] }];
+set_property -dict { PACKAGE_PIN V16 IOSTANDARD LVCMOS33 } [get_ports { shield_io[27] }];
+set_property -dict { PACKAGE_PIN M13 IOSTANDARD LVCMOS33 } [get_ports { shield_io[28] }];
+set_property -dict { PACKAGE_PIN R10 IOSTANDARD LVCMOS33 } [get_ports { shield_io[29] }];
+set_property -dict { PACKAGE_PIN R11 IOSTANDARD LVCMOS33 } [get_ports { shield_io[30] }];
+set_property -dict { PACKAGE_PIN R13 IOSTANDARD LVCMOS33 } [get_ports { shield_io[31] }];
+set_property -dict { PACKAGE_PIN R15 IOSTANDARD LVCMOS33 } [get_ports { shield_io[32] }];
+set_property -dict { PACKAGE_PIN P15 IOSTANDARD LVCMOS33 } [get_ports { shield_io[33] }];
+set_property -dict { PACKAGE_PIN R16 IOSTANDARD LVCMOS33 } [get_ports { shield_io[34] }];
+set_property -dict { PACKAGE_PIN N16 IOSTANDARD LVCMOS33 } [get_ports { shield_io[35] }];
+set_property -dict { PACKAGE_PIN N14 IOSTANDARD LVCMOS33 } [get_ports { shield_io[36] }];
+set_property -dict { PACKAGE_PIN U17 IOSTANDARD LVCMOS33 } [get_ports { shield_io[37] }];
+set_property -dict { PACKAGE_PIN T18 IOSTANDARD LVCMOS33 } [get_ports { shield_io[38] }];
+set_property -dict { PACKAGE_PIN R18 IOSTANDARD LVCMOS33 } [get_ports { shield_io[39] }];
+set_property -dict { PACKAGE_PIN P18 IOSTANDARD LVCMOS33 } [get_ports { shield_io[40] }];
+set_property -dict { PACKAGE_PIN N17 IOSTANDARD LVCMOS33 } [get_ports { shield_io[41] }];
+set_property -dict { PACKAGE_PIN M17 IOSTANDARD LVCMOS33 } [get_ports { shield_io[42] }]; # A
+set_property -dict { PACKAGE_PIN L18 IOSTANDARD LVCMOS33 } [get_ports { shield_io[43] }]; # SCL
+set_property -dict { PACKAGE_PIN M18 IOSTANDARD LVCMOS33 } [get_ports { shield_io[44] }]; # SDA
 #set_property -dict { PACKAGE_PIN C2  IOSTANDARD LVCMOS33 } [get_ports { shield_rst }];
 
 #set_property -dict { PACKAGE_PIN C1  IOSTANDARD LVCMOS33 } [get_ports { spi_hdr_ss }];
index 68d1e898250bb71fa2bd87deb42add89e8466c16..a6117d8498ae9f49979fa60856c7003199f51bb9 100644 (file)
@@ -26,7 +26,8 @@ entity toplevel is
         LOG_LENGTH         : natural := 512;
         USE_LITEETH        : boolean  := false;
         UART_IS_16550      : boolean  := false;
-        HAS_UART1          : boolean  := true
+        HAS_UART1          : boolean  := true;
+        NGPIO              : natural := 32
         );
     port(
         ext_clk   : in  std_ulogic;
@@ -59,6 +60,9 @@ entity toplevel is
         spi_flash_wp_n   : inout std_ulogic;
         spi_flash_hold_n : inout std_ulogic;
 
+        -- GPIO
+        shield_io        : inout std_ulogic_vector(44 downto 0);
+
         -- Ethernet
         eth_ref_clk      : out std_ulogic;
         eth_clocks_tx    : in std_ulogic;
@@ -140,6 +144,11 @@ architecture behaviour of toplevel is
     signal spi_sdat_oe : std_ulogic_vector(3 downto 0);
     signal spi_sdat_i  : std_ulogic_vector(3 downto 0);
 
+    -- GPIO
+    signal gpio_in     : std_ulogic_vector(NGPIO - 1 downto 0);
+    signal gpio_out    : std_ulogic_vector(NGPIO - 1 downto 0);
+    signal gpio_dir    : std_ulogic_vector(NGPIO - 1 downto 0);
+
     -- Fixup various memory sizes based on generics
     function get_bram_size return natural is
     begin
@@ -184,7 +193,8 @@ begin
             LOG_LENGTH         => LOG_LENGTH,
             HAS_LITEETH        => USE_LITEETH,
             UART0_IS_16550     => UART_IS_16550,
-            HAS_UART1          => HAS_UART1
+            HAS_UART1          => HAS_UART1,
+            NGPIO              => NGPIO
             )
         port map (
             -- System signals
@@ -206,6 +216,11 @@ begin
             spi_flash_sdat_oe => spi_sdat_oe,
             spi_flash_sdat_i  => spi_sdat_i,
 
+            -- GPIO signals
+            gpio_in           => gpio_in,
+            gpio_out          => gpio_out,
+            gpio_dir          => gpio_dir,
+
             -- External interrupts
             ext_irq_eth       => ext_irq_eth,
 
@@ -545,6 +560,72 @@ begin
     led4 <= system_clk_locked;
     led5 <= eth_clk_locked;
     led6 <= not soc_rst;
-    led7 <= not spi_flash_cs_n;
+
+    -- GPIO
+    gpio_in(0) <= shield_io(0);
+    gpio_in(1) <= shield_io(1);
+    gpio_in(2) <= shield_io(2);
+    gpio_in(3) <= shield_io(3);
+    gpio_in(4) <= shield_io(4);
+    gpio_in(5) <= shield_io(5);
+    gpio_in(6) <= shield_io(6);
+    gpio_in(7) <= shield_io(7);
+    gpio_in(8) <= shield_io(8);
+    gpio_in(9) <= shield_io(9);
+    gpio_in(10) <= shield_io(10);
+    gpio_in(11) <= shield_io(11);
+    gpio_in(12) <= shield_io(12);
+    gpio_in(13) <= shield_io(13);
+    gpio_in(14) <= shield_io(26);
+    gpio_in(15) <= shield_io(27);
+    gpio_in(16) <= shield_io(28);
+    gpio_in(17) <= shield_io(29);
+    gpio_in(18) <= shield_io(30);
+    gpio_in(19) <= shield_io(31);
+    gpio_in(20) <= shield_io(32);
+    gpio_in(21) <= shield_io(33);
+    gpio_in(22) <= shield_io(34);
+    gpio_in(23) <= shield_io(35);
+    gpio_in(24) <= shield_io(36);
+    gpio_in(25) <= shield_io(37);
+    gpio_in(26) <= shield_io(38);
+    gpio_in(27) <= shield_io(39);
+    gpio_in(28) <= shield_io(40);
+    gpio_in(29) <= shield_io(41);
+    gpio_in(30) <= shield_io(42);
+    gpio_in(31) <= gpio_out(31);
+
+    shield_io(0) <= gpio_out(0) when gpio_dir(0) = '1' else 'Z';
+    shield_io(1) <= gpio_out(1) when gpio_dir(1) = '1' else 'Z';
+    shield_io(2) <= gpio_out(2) when gpio_dir(2) = '1' else 'Z';
+    shield_io(3) <= gpio_out(3) when gpio_dir(3) = '1' else 'Z';
+    shield_io(4) <= gpio_out(4) when gpio_dir(4) = '1' else 'Z';
+    shield_io(5) <= gpio_out(5) when gpio_dir(5) = '1' else 'Z';
+    shield_io(6) <= gpio_out(6) when gpio_dir(6) = '1' else 'Z';
+    shield_io(7) <= gpio_out(7) when gpio_dir(7) = '1' else 'Z';
+    shield_io(8) <= gpio_out(8) when gpio_dir(8) = '1' else 'Z';
+    shield_io(9) <= gpio_out(9) when gpio_dir(9) = '1' else 'Z';
+    shield_io(10) <= gpio_out(10) when gpio_dir(10) = '1' else 'Z';
+    shield_io(11) <= gpio_out(11) when gpio_dir(11) = '1' else 'Z';
+    shield_io(12) <= gpio_out(12) when gpio_dir(12) = '1' else 'Z';
+    shield_io(13) <= gpio_out(13) when gpio_dir(13) = '1' else 'Z';
+    shield_io(26) <= gpio_out(14) when gpio_dir(14) = '1' else 'Z';
+    shield_io(27) <= gpio_out(15) when gpio_dir(15) = '1' else 'Z';
+    shield_io(28) <= gpio_out(16) when gpio_dir(16) = '1' else 'Z';
+    shield_io(29) <= gpio_out(17) when gpio_dir(17) = '1' else 'Z';
+    shield_io(30) <= gpio_out(18) when gpio_dir(18) = '1' else 'Z';
+    shield_io(31) <= gpio_out(19) when gpio_dir(19) = '1' else 'Z';
+    shield_io(32) <= gpio_out(20) when gpio_dir(20) = '1' else 'Z';
+    shield_io(33) <= gpio_out(21) when gpio_dir(21) = '1' else 'Z';
+    shield_io(34) <= gpio_out(22) when gpio_dir(22) = '1' else 'Z';
+    shield_io(35) <= gpio_out(23) when gpio_dir(23) = '1' else 'Z';
+    shield_io(36) <= gpio_out(24) when gpio_dir(24) = '1' else 'Z';
+    shield_io(37) <= gpio_out(25) when gpio_dir(25) = '1' else 'Z';
+    shield_io(38) <= gpio_out(26) when gpio_dir(26) = '1' else 'Z';
+    shield_io(39) <= gpio_out(27) when gpio_dir(27) = '1' else 'Z';
+    shield_io(40) <= gpio_out(28) when gpio_dir(28) = '1' else 'Z';
+    shield_io(41) <= gpio_out(29) when gpio_dir(29) = '1' else 'Z';
+    shield_io(42) <= gpio_out(30) when gpio_dir(30) = '1' else 'Z';
+    led7 <= gpio_out(31) and gpio_dir(31);
 
 end architecture behaviour;
diff --git a/gpio.vhdl b/gpio.vhdl
new file mode 100644 (file)
index 0000000..bdce519
--- /dev/null
+++ b/gpio.vhdl
@@ -0,0 +1,99 @@
+-- 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";
+
+    -- Current output value and direction
+    signal reg_data : std_ulogic_vector(NGPIO - 1 downto 0) := (others => '0');
+    signal reg_dirn : std_ulogic_vector(NGPIO - 1 downto 0) := (others => '0');
+    signal reg_in1  : std_ulogic_vector(NGPIO - 1 downto 0);
+    signal reg_in2  : std_ulogic_vector(NGPIO - 1 downto 0);
+
+    signal wb_rsp   : wb_io_slave_out;
+    signal reg_out  : std_ulogic_vector(NGPIO - 1 downto 0);
+
+begin
+
+    -- No interrupt facility for now
+    intr <= '0';
+
+    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 2) select reg_out <=
+        reg_data when GPIO_REG_DATA_OUT,
+        reg_in2  when GPIO_REG_DATA_IN,
+        reg_dirn when GPIO_REG_DIR,
+        (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)
+    begin
+        if rising_edge(clk) then
+            wb_out <= wb_rsp;
+            reg_in2 <= reg_in1;
+            reg_in1 <= gpio_in;
+            if rst = '1' then
+                reg_data <= (others => '0');
+                reg_dirn <= (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 2) 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 others =>
+                    end case;
+                end if;
+            end if;
+        end if;
+    end process;
+
+end architecture behaviour;
+        
index 79af3c12bc72cb762cb25461e1e9e04737db760a..9da83137740cab24aba190a133838c0aeb6fc845 100644 (file)
@@ -44,6 +44,7 @@ filesets:
       - wishbone_bram_wrapper.vhdl
       - soc.vhdl
       - xics.vhdl
+      - gpio.vhdl
       - syscon.vhdl
       - sync_fifo.vhdl
       - spi_rxtx.vhdl
index 88249bf51e92d91a6a6b70df65ce4055627cb3f8..2bf3b726c4c564412b6f180ba9ac2fd17a3064c1 100644 (file)
--- a/soc.vhdl
+++ b/soc.vhdl
@@ -24,6 +24,7 @@ use work.wishbone_types.all;
 -- 0xc0004000: XICS ICP
 -- 0xc0005000: XICS ICS
 -- 0xc0006000: SPI Flash controller
+-- 0xc0007000: GPIO controller
 -- 0xc8nnnnnn: External IO bus
 -- 0xf0000000: Flash "ROM" mapping
 -- 0xff000000: DRAM init code (if any) or flash ROM (**)
@@ -66,7 +67,8 @@ entity soc is
         LOG_LENGTH         : natural := 512;
         HAS_LITEETH        : boolean := false;
        UART0_IS_16550     : boolean := true;
-       HAS_UART1          : boolean := false
+       HAS_UART1          : boolean := false;
+        NGPIO              : natural := 0
        );
     port(
        rst          : in  std_ulogic;
@@ -101,6 +103,11 @@ entity soc is
         spi_flash_sdat_oe : out std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0);
         spi_flash_sdat_i  : in  std_ulogic_vector(SPI_FLASH_DLINES-1 downto 0) := (others => '1');
 
+        -- GPIO signals
+        gpio_out : out std_ulogic_vector(NGPIO - 1 downto 0);
+        gpio_dir : out std_ulogic_vector(NGPIO - 1 downto 0);
+        gpio_in  : in  std_ulogic_vector(NGPIO - 1 downto 0) := (others => '0');
+
        -- DRAM controller signals
        alt_reset    : in std_ulogic := '0'
        );
@@ -167,6 +174,11 @@ architecture behaviour of soc is
     signal ics_to_icp       : ics_to_icp_t;
     signal core_ext_irq     : std_ulogic;
 
+    -- GPIO signals:
+    signal wb_gpio_in    : wb_io_master_out;
+    signal wb_gpio_out   : wb_io_slave_out;
+    signal gpio_intr     : std_ulogic := '0';
+
     -- Main memory signals:
     signal wb_bram_in     : wishbone_master_out;
     signal wb_bram_out    : wishbone_slave_out;
@@ -192,6 +204,7 @@ architecture behaviour of soc is
     signal rst_uart    : std_ulogic := '1';
     signal rst_xics    : std_ulogic := '1';
     signal rst_spi     : std_ulogic := '1';
+    signal rst_gpio    : std_ulogic := '1';
     signal rst_bram    : std_ulogic := '1';
     signal rst_dtm     : std_ulogic := '1';
     signal rst_wbar    : std_ulogic := '1';
@@ -206,6 +219,7 @@ architecture behaviour of soc is
                            SLAVE_IO_UART1,
                            SLAVE_IO_SPI_FLASH_REG,
                            SLAVE_IO_SPI_FLASH_MAP,
+                           SLAVE_IO_GPIO,
                            SLAVE_IO_EXTERNAL,
                            SLAVE_IO_NONE);
     signal slave_io_dbg : slave_io_type;
@@ -243,6 +257,7 @@ begin
             rst_uart    <= rst;
             rst_spi     <= rst;
             rst_xics    <= rst;
+            rst_gpio    <= rst;
             rst_bram    <= rst;
             rst_dtm     <= rst;
             rst_wbar    <= rst;
@@ -530,6 +545,8 @@ begin
            slave_io := SLAVE_IO_ICS;
        elsif std_match(match, x"C0006") then
            slave_io := SLAVE_IO_SPI_FLASH_REG;
+        elsif std_match(match, x"C0007") then
+            slave_io := SLAVE_IO_GPIO;
        end if;
         slave_io_dbg <= slave_io;
        wb_uart0_in <= wb_sio_out;
@@ -540,6 +557,8 @@ begin
        wb_spiflash_in.cyc <= '0';
         wb_spiflash_is_reg <= '0';
         wb_spiflash_is_map <= '0';
+        wb_gpio_in <= wb_sio_out;
+        wb_gpio_in.cyc <= '0';
 
         -- Only give xics 8 bits of wb addr (for now...)
        wb_xics_icp_in <= wb_sio_out;
@@ -619,6 +638,9 @@ begin
            wb_spiflash_in.cyc <= wb_sio_out.cyc;
            wb_sio_in <= wb_spiflash_out;
             wb_spiflash_is_reg <= '1';
+        when SLAVE_IO_GPIO =>
+            wb_gpio_in.cyc <= wb_sio_out.cyc;
+            wb_sio_in <= wb_gpio_out;
        when others =>
        end case;
 
@@ -811,6 +833,21 @@ begin
             icp_out => ics_to_icp
            );
 
+    gpio : entity work.gpio
+        generic map(
+            NGPIO => NGPIO
+            )
+        port map(
+            clk      => system_clk,
+            rst      => rst_gpio,
+            wb_in    => wb_gpio_in,
+            wb_out   => wb_gpio_out,
+            gpio_in  => gpio_in,
+            gpio_out => gpio_out,
+            gpio_dir => gpio_dir,
+            intr     => gpio_intr
+            );
+
     -- Assign external interrupts
     interrupts: process(all)
     begin
@@ -818,6 +855,7 @@ begin
         int_level_in(0) <= uart0_irq;
         int_level_in(1) <= ext_irq_eth;
         int_level_in(2) <= uart1_irq;
+        int_level_in(3) <= gpio_intr;
     end process;
 
     -- BRAM Memory slave