basic structure of tests in place

Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
diff --git a/.gitignore b/.gitignore
index 0a1f620..c5d161b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,6 @@
 packages/
 simv.*
 obj_dir/
+__pycache__/
+verilog/gl/
 
diff --git a/README.md b/README.md
index 110acfb..fb112a1 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,113 @@
-# open_mpw_user_project
-Test project for the Open MPW shuttle
+# FWPayload
+
+FWPayload is a small processor+peripherals subsystem, targeting the user-project
+area of Caraval.
+
+## Block diagram
+![FWPayload Block Diagram](doc/images/fwpayload_diagram.png)
+
+
+## External IP
+FWPayload uses several pieces of external IP. Some are bundled with the project,
+and some are fetched during the initialization step.
+
+### FWRISC
+RISC-V core originally targeted for FPGA application
+- Git: https://github.com/mballance/fwrisc.git
+- License: Apache 2.0
+
+### fw-wishbone-interconnect
+Parameterized Wishbone interconnect
+- Git: https://github.com/featherweight-ip/fw-wishbone-interconnect
+- License: Apache 2.0
+
+### simple_spi_master
+SPI master IP, obtained from the Caravel repository. Bundled with the project.
+- License: GNU LGPL
+
+### simpleuart
+UART IP, obtained from the Caravel repository. Bundled with the project.
+- License: BSD-style
+
+
+## Memory map
+
+The FWPayload memory map is designed to fit within the 28-bit user-area
+portion of the Caravel memory map. 
+
+- *0xX000_0000..0xX000_03FF* - 1Kb register RAM
+- *0xX100_0000..0xX100_00FF* - UART
+- *0xX100_0100..0xX100_01FF* - SPI
+- *0xX100_0200..0xX100_02FF* - GPIO
+
+## Pin map
+
+## Bring-up/Debug Support
+
+FWPayload uses the Caravel logic analyzer to configure reset and clocking,
+probe the program counter of the FWRISC, and optionally, single-step the clock.
+
+- [127]   - Controls the clock when configured as an output
+- [126]   - Controls the system reset when configured as an output
+- [125]   - Controls the FWRISC core reset when configured as an output
+- [39:36] - Loopback, probing the GPIO output
+- [33]    - Loopback, probing UART tx output
+- [32]    - Input, probing the 'instruction-complete' FWRISC net
+- [31:0]  - Input, probing the FWRISC program-counter net
+
+# Developer Notes
+
+## Required Tools
+- Python 3       (3.6.8 was used)
+- Icarus Verilog (11.0 was used)
+- Verilator      (4.102 was used)
+- Openlane       (rc4 was used)
+- Skywater PDK   (PDK_ROOT is assumed to be properly set)
+
+## Project Setup
+The FWPayload project uses IVPM (IP and Verification Package Manager) to manage
+external IP and Python dependencies. The project can be setup both with and
+without IVPM installed.
+
+In both cases, setting up the project will result in creation of a `packages`
+directory within the project that contains external IPs and required Python
+packages.
+
+### Setup with IVPM installed
+Ensure IVPM is installed:
+
+```
+% pip3 install ivpm --user --upgrade
+```
+
+```
+% cd <fwpayload_dir>
+% ivpm update
+```
+
+### Setup without IVPM installed
+The project can also be setup without installing IVPM. The `bootstrap.sh` 
+script is provided for this purpose. `bootstrap.sh` clones a local 
+copy of ivpm.
+
+```
+% cd <fwpayload_dir>
+% ./bootstrap.sh
+```
+
+
+## Integration Testing
+
+
+## Current Status
+Openlane completes with the following status:
+
+```
+Number of pins violated: 479
+Number of nets violated: 297
+Total number of nets: 44367
+[INFO]: Generating Final Summary Report...
+[SUCCESS]: Flow Completed Without Fatal Errors.
+```
+
+
diff --git a/doc/images/fwpayload_diagram.png b/doc/images/fwpayload_diagram.png
new file mode 100644
index 0000000..b62b6a6
--- /dev/null
+++ b/doc/images/fwpayload_diagram.png
Binary files differ
diff --git a/dv/bringup/.gitignore b/dv/bringup/.gitignore
new file mode 100644
index 0000000..a8689d9
--- /dev/null
+++ b/dv/bringup/.gitignore
@@ -0,0 +1,3 @@
+
+pybfms_gen*
+
diff --git a/dv/bringup/Makefile b/dv/bringup/Makefile
index edbc363..7ff1b89 100644
--- a/dv/bringup/Makefile
+++ b/dv/bringup/Makefile
@@ -8,13 +8,18 @@
 #********************************************************************
 FWRISC_SRCS = $(wildcard $(FWRISC_RTL_DIR)/*.sv)
 INCDIRS += $(FWRISC_RTL_DIR)
+INCDIRS += $(PACKAGES_DIR)/fwprotocol-defs/src/sv
 
 DEFINES += MPRJ_IO_PADS=38
 PYBFMS_MODULES += wishbone_bfms logic_analyzer_bfms
 SRCS += $(RTL_DIR)/fwpayload.v $(RTL_DIR)/user_project_wrapper.v
-SRCS += $(RTL_DIR)/wb_interconnect_NxN.sv
+SRCS += $(PACKAGES_DIR)/fw-wishbone-interconnect/verilog/rtl/wb_interconnect_NxN.v
+SRCS += $(PACKAGES_DIR)/fw-wishbone-interconnect/verilog/rtl/wb_interconnect_arb.v
 SRCS += $(RTL_DIR)/spram_32x256.sv
+SRCS += $(RTL_DIR)/spram_32x512.sv
 SRCS += $(RTL_DIR)/spram.v
+SRCS += $(RTL_DIR)/simple_spi_master.v
+SRCS += $(RTL_DIR)/simpleuart.v
 SRCS += $(FWRISC_SRCS) 
 
 TOP_MODULE = bringup_tb
diff --git a/dv/bringup/python/bringup_tests/__pycache__/mgmt_mem_access.cpython-36.pyc b/dv/bringup/python/bringup_tests/__pycache__/mgmt_mem_access.cpython-36.pyc
deleted file mode 100644
index ca45710..0000000
--- a/dv/bringup/python/bringup_tests/__pycache__/mgmt_mem_access.cpython-36.pyc
+++ /dev/null
Binary files differ
diff --git a/dv/bringup/python/bringup_tests/la_utils.py b/dv/bringup/python/bringup_tests/la_utils.py
index e3c63b3..c0f996b 100644
--- a/dv/bringup/python/bringup_tests/la_utils.py
+++ b/dv/bringup/python/bringup_tests/la_utils.py
@@ -5,9 +5,11 @@
 '''
 
 class LaUtils(object):
-    CLOCK_IDX = 127
-    RESET_IDX = 126
+    CLOCK_IDX      = 127
+    RESET_IDX      = 126
     CORE_RESET_IDX = 125
+    PC_IDX         = 0
+    GPIO_OUT_IDX   = 36
     
     def __init__(self, la_bfm):
         self.la_bfm = la_bfm
diff --git a/dv/bringup/python/bringup_tests/mgmt_mem_access.py b/dv/bringup/python/bringup_tests/mgmt_mem_access.py
index 1937cc8..c265bd4 100644
--- a/dv/bringup/python/bringup_tests/mgmt_mem_access.py
+++ b/dv/bringup/python/bringup_tests/mgmt_mem_access.py
@@ -50,6 +50,23 @@
         else:
             print("FAIL: " + hex(0x80000000+4*i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
 
+    # Load a short program that toggles the GPIO lines
+    gpio_toggle_program = [
+        0x010000b7,
+        0x20008093,
+        0x00000113,
+        0x0020a023,
+        0x00110113,
+        0xff9ff06f,
+        0x00000000]
+    
+    for i,data in enumerate(gpio_toggle_program):
+        print("Write: " + hex(0x30000000+4*i) + " " + hex(data))
+        await u_wb.write(0x30000000+4*i, data, 0xF)
+
+    # Take back clock control    
+    await la_utils.set_dut_clock_control(True)
+    
     # Release the processor from reset
     await la_utils.set_core_reset(True)
     for i in range(10):
@@ -57,5 +74,6 @@
     await la_utils.set_core_reset(False)
 
     for i in range(1000):
-        await u_la.propagate()
+        await la_utils.clock_dut()
+
         
\ No newline at end of file
diff --git a/dv/common/defs_rules.mk b/dv/common/defs_rules.mk
new file mode 100644
index 0000000..7d87f53
--- /dev/null
+++ b/dv/common/defs_rules.mk
@@ -0,0 +1,58 @@
+
+COMMON_DIR    := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+
+ifneq (1,$(RULES))
+RTL_DIR       := $(abspath $(COMMON_DIR)/../../verilog/rtl)
+GL_DIR        := $(abspath $(COMMON_DIR)/../../verilog/gl)
+PACKAGES_DIR  := $(abspath $(COMMON_DIR)/../../packages)
+SIM ?= icarus
+SIMTYPE ?= functional
+
+
+PYBFMS_MODULES += wishbone_bfms logic_analyzer_bfms
+VLSIM_CLKSPEC += -clkspec clk=10ns
+
+TOP_MODULE ?= fwpayload_tb
+TB_SRCS ?= $(COMMON_DIR)/sv/fwpayload_tb.sv
+
+PYTHONPATH := $(COMMON_DIR)/python:$(PYTHONPATH)
+export PYTHONPATH
+
+PATH := $(PACKAGES_DIR)/python/bin:$(PATH)
+export PATH
+
+#********************************************************************
+#* Source setup
+#********************************************************************
+FWRISC_SRCS = $(wildcard $(PACKAGES_DIR)/fwrisc/rtl/*.sv)
+INCDIRS += $(PACKAGES_DIR)/fwrisc/rtl
+INCDIRS += $(PACKAGES_DIR)/fwprotocol-defs/src/sv
+
+DEFINES += MPRJ_IO_PADS=38
+
+ifeq (gate,$(SIMTYPE))
+SRCS += $(GL_DIR)/fwpayload.v
+else
+SRCS += $(RTL_DIR)/fwpayload.v
+SRCS += $(PACKAGES_DIR)/fw-wishbone-interconnect/verilog/rtl/wb_interconnect_NxN.v
+SRCS += $(PACKAGES_DIR)/fw-wishbone-interconnect/verilog/rtl/wb_interconnect_arb.v
+SRCS += $(RTL_DIR)/spram_32x256.sv
+SRCS += $(RTL_DIR)/spram_32x512.sv
+SRCS += $(RTL_DIR)/spram.v
+SRCS += $(RTL_DIR)/simple_spi_master.v
+SRCS += $(RTL_DIR)/simpleuart.v
+SRCS += $(FWRISC_SRCS) 
+endif
+SRCS += $(TB_SRCS)
+
+include $(COMMON_DIR)/$(SIM).mk
+
+else # Rules
+
+clean ::
+	rm -f results.xml
+
+include $(COMMON_DIR)/$(SIM).mk
+include $(wildcard $(COMMON_DIR)/*_clean.mk)
+
+endif
\ No newline at end of file
diff --git a/dv/common/icarus.mk b/dv/common/icarus.mk
index d9b319c..9ff0b03 100644
--- a/dv/common/icarus.mk
+++ b/dv/common/icarus.mk
@@ -14,6 +14,8 @@
 #****************************************************************************
 
 COMMON_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+
+ifneq (1,$(RULES))
 PACKAGES_DIR := $(abspath $(COMMON_DIR)/../../packages)
 VLSIM := $(PACKAGES_DIR)/python/bin/vlsim
 PYBFMS_VPI_LIB := $(shell $(PACKAGES_DIR)/python/bin/pybfms lib)
@@ -54,6 +56,8 @@
 VPI_LIBS += $(PYBFMS_VPI_LIB)
 VPI_LIBS += $(COCOTB_PREFIX)/cocotb/libs/libcocotbvpi_icarus.vpl
 
+else # Rules
+
 build : $(SIMV)
 
 $(SIMV) : $(SRCS) pybfms_gen.v
@@ -66,6 +70,4 @@
 	$(PACKAGES_DIR)/python/bin/pybfms generate \
 		-l vlog $(foreach m,$(PYBFMS_MODULES),-m $(m)) -o $@
 
-clean ::
-	rm -f simv.* simx.fst simx.vcd pybfms_gen.v
-	rm -rf obj_dir
+endif
diff --git a/dv/common/icarus_clean.mk b/dv/common/icarus_clean.mk
new file mode 100644
index 0000000..3dccc2d
--- /dev/null
+++ b/dv/common/icarus_clean.mk
@@ -0,0 +1,9 @@
+#****************************************************************************
+#* icarus_clean.mk
+#*
+#* Clean target for Icarus Verilog
+#****************************************************************************
+
+clean ::
+	rm -f simv.* simx.fst simx.vcd pybfms_gen.v
+	rm -rf obj_dir
diff --git a/dv/common/python/fwpayload_tests/la_utils.py b/dv/common/python/fwpayload_tests/la_utils.py
new file mode 100644
index 0000000..c0f996b
--- /dev/null
+++ b/dv/common/python/fwpayload_tests/la_utils.py
@@ -0,0 +1,59 @@
+'''
+Created on Nov 22, 2020
+
+@author: mballance
+'''
+
+class LaUtils(object):
+    CLOCK_IDX      = 127
+    RESET_IDX      = 126
+    CORE_RESET_IDX = 125
+    PC_IDX         = 0
+    GPIO_OUT_IDX   = 36
+    
+    def __init__(self, la_bfm):
+        self.la_bfm = la_bfm
+        
+    async def set_dut_clock_control(self, en):
+        # First, set reset high and clock low
+        await self.la_bfm.set_bits(LaUtils.RESET_IDX, 0, 1)
+        await self.la_bfm.set_bits(LaUtils.CLOCK_IDX, 0, 1)
+        
+        if en:
+            # Now, set output mode for these signals
+            await self.la_bfm.set_oen(LaUtils.RESET_IDX, 0, 1)
+            await self.la_bfm.set_oen(LaUtils.CLOCK_IDX, 0, 1)
+        else:
+            # Now, set input mode for these signals
+            await self.la_bfm.set_oen(LaUtils.RESET_IDX, 1, 1)
+            await self.la_bfm.set_oen(LaUtils.CLOCK_IDX, 1, 1)
+        
+    async def set_core_reset(self, en):
+        if en:
+            await self.la_bfm.set_bits(LaUtils.CORE_RESET_IDX, 0, 1)
+        else:
+            await self.la_bfm.set_bits(LaUtils.CORE_RESET_IDX, 1, 1)
+        
+    async def reset_cycle_dut(self, cycles=10):
+        # Set reset high
+        await self.la_bfm.set_bits(LaUtils.RESET_IDX, 0, 1)
+        await self.la_bfm.propagate()
+        
+        # Clock 
+        for i in range(cycles):
+            await self.clock_dut()
+            
+        # Set reset low
+        await self.la_bfm.set_bits(LaUtils.RESET_IDX, 1, 1)
+        await self.clock_dut()
+        
+        pass
+
+    async def clock_dut(self):
+        await self.la_bfm.set_bits(LaUtils.CLOCK_IDX, 1, 1)
+        await self.la_bfm.propagate()
+        await self.la_bfm.set_bits(LaUtils.CLOCK_IDX, 0, 1)
+        await self.la_bfm.propagate()
+
+        
+        
\ No newline at end of file
diff --git a/dv/common/python/fwpayload_tests/mgmt_mem_access.py b/dv/common/python/fwpayload_tests/mgmt_mem_access.py
new file mode 100644
index 0000000..e6da439
--- /dev/null
+++ b/dv/common/python/fwpayload_tests/mgmt_mem_access.py
@@ -0,0 +1,101 @@
+'''
+Created on Nov 28, 2020
+
+@author: mballance
+'''
+
+import cocotb
+import pybfms
+from wishbone_bfms.wb_initiator_bfm import WbInitiatorBfm
+from logic_analyzer_bfms.la_initiator_bfm import LaInitiatorBfm
+from random import Random
+from fwpayload_tests.la_utils import LaUtils
+
+@cocotb.test()
+async def test(top):
+    """
+    Hold the payload DUT in reset via the logic analyzer
+    Meanwhile, test that the management interface can access memory
+    """
+    await pybfms.init()
+    u_wb : WbInitiatorBfm = pybfms.find_bfm(".*u_wb")
+    u_la : LaInitiatorBfm = pybfms.find_bfm(".*u_la")
+    
+    # Bring the system out of reset, while leaving the
+    # FWRISC core in reset
+    la_utils = LaUtils(u_la)
+    await la_utils.reset_cycle_dut(100)
+    await la_utils.set_dut_clock_control(False)
+    
+    # Test that we can write and read dut 'ROM'
+    n_fails = 0
+    r = Random(0)
+    
+    # First, do word accesses
+    print("** Testing Word Accesses")
+    wr_data = []
+    for i in range(16):
+        data = r.randint(0, 0xFFFFFFFF)
+        print("Write: " + hex(0x30000000+4*i) + " = " + hex(data))
+        await u_wb.write(0x30000000 + 4*i, data, 0xF)
+        wr_data.append(data)
+        print("wr_data[" + str(i) + "] = " + hex(wr_data[i]))
+        
+    for i in range(16):
+        data = await u_wb.read(0x30000000 + 4*i)
+        if wr_data[i] == data:
+            print("PASS: " + hex(0x30000000+4*i))
+        else:
+            print("FAIL: " + hex(0x30000000+4*i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
+            raise cocotb.result.TestError(
+                "Addr: " + hex(0x30000000+4*i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
+            n_fails += 1
+
+    # First, do word accesses
+    print("** Testing Half-word Accesses")
+    wr_data = []
+    for i in range(32):
+        data = r.randint(0, 0xFFFF)
+        wr_data.append(data)
+        data <<= (16*(i%2))
+        print("Write: " + hex(0x30000000+2*i) + " = " + hex(data))
+        await u_wb.write(0x30000000 + 2*i, data, 
+                         0x3 if (i%2) == 0 else 0xC)
+        print("wr_data[" + str(i) + "] = " + hex(wr_data[i]))
+        
+    for i in range(32):
+        data = await u_wb.read(0x30000000 + 2*i)
+        if (i%2) != 0:
+            data >>= 16
+        data &= 0xFFFF
+        if wr_data[i] == data:
+            print("PASS: " + hex(0x30000000+2*i))
+        else:
+            print("FAIL: " + hex(0x30000000+2*i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
+            raise cocotb.result.TestError(
+                "Addr: " + hex(0x30000000+2*i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
+            n_fails += 1
+
+    # First, do word accesses
+    print("** Testing Byte Accesses")
+    wr_data = []
+    for i in range(64):
+        data = r.randint(0, 0xFF)
+        wr_data.append(data)
+        data <<= (8*(i%4))
+        print("Write: " + hex(0x30000000+i) + " = " + hex(data))
+        await u_wb.write(0x30000000 + i, data, (1 << (i%4)))
+        print("wr_data[" + str(i) + "] = " + hex(wr_data[i]))
+        
+    for i in range(64):
+        data = await u_wb.read(0x30000000 + i)
+        data >>= (8*(i%4))
+        data &= 0xFF
+        if wr_data[i] == data:
+            print("PASS: " + hex(0x30000000+i))
+        else:
+            print("FAIL: " + hex(0x30000000+i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
+            raise cocotb.result.TestError(
+                "Addr: " + hex(0x30000000+i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
+            n_fails += 1
+
diff --git a/dv/common/questa.mk b/dv/common/questa.mk
index e68ccba..d0532b9 100644
--- a/dv/common/questa.mk
+++ b/dv/common/questa.mk
@@ -14,32 +14,36 @@
 #* TIMEOUT        - Simulation timeout, in units of ns,us,ms,s
 #****************************************************************************
 
+ifneq (1,$(RULES))
 COMMON_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
 PACKAGES_DIR := $(abspath $(COMMON_DIR)/../../packages)
+PYBFMS_DPI_LIB := $(subst .so,,$(shell $(PACKAGES_DIR)/python/bin/pybfms lib))
+COCOTB_PREFIX := $(shell $(PACKAGES_DIR)/python/bin/cocotb-config --prefix)
 
-ifneq (,$(DEBUG))
-VLSIM_OPTIONS += --trace-fst
-SIMV_ARGS += +vlsim.trace
-SIMV := simv.debug
-else
-SIMV := simv.ndebug
-endif
+DPI_LIBS += $(PYBFMS_DPI_LIB)
+VPI_LIBS += $(COCOTB_PREFIX)/cocotb/libs/libcocotbvpi_modelsim.so
 
-# Enable VPI for Verilator
-VLSIM_OPTIONS += --vpi --public-flat-rw
+VLOG_OPTIONS += $(foreach inc,$(INCDIRS),+incdir+$(inc))
+VLOG_OPTIONS += $(foreach def,$(DEFINES),+define+$(def))
+VSIM_OPTIONS += $(foreach vpi,$(VPI_LIBS),-pli $(vpi))
+VSIM_OPTIONS += $(foreach dpi,$(DPI_LIBS),-sv_lib $(dpi))
 
-VLSIM_OPTIONS += $(foreach inc,$(INCDIRS),+incdir+$(inc))
-VLSIM_OPTIONS += $(foreach def,$(DEFINES),+define+$(def))
-SIMV_ARGS += $(foreach vpi,$(VPI_LIBS),+vpi=$(vpi))
+SRCS += pybfms_gen.sv pybfms_gen.c
 
-build : $(SIMV)
+else # Rules
 
-$(SIMV) : $(SRCS)
-	$(VLSIM) -o $@ $(VLSIM_CLKSPEC) $(VLSIM_OPTIONS) $(SRCS)
+build : $(SRCS)
+	vlib work
+	vlog $(VLOG_OPTIONS) $(SRCS)
+	vopt -o $(TOP_MODULE)_opt $(TOP_MODULE)
 
-run : $(SIMV)
-	./$(SIMV) $(SIMV_ARGS)
 
-clean ::
-	rm -f simv.* simx.fst simx.vcd
-	rm -rf obj_dir
+run : build
+	vsim -batch -do "run $(TIMEOUT); quit -f" \
+	$(VSIM_OPTIONS) $(TOP_MODULE)_opt
+
+pybfms_gen.sv pybfms_gen.c :
+	$(PACKAGES_DIR)/python/bin/pybfms generate \
+		-l sv $(foreach m,$(PYBFMS_MODULES),-m $(m)) -o $@
+
+endif
\ No newline at end of file
diff --git a/dv/common/questa_clean.mk b/dv/common/questa_clean.mk
new file mode 100644
index 0000000..dd44cdf
--- /dev/null
+++ b/dv/common/questa_clean.mk
@@ -0,0 +1,12 @@
+#****************************************************************************
+#* questa_target.mk
+#*
+#* Clean target for Mentor Questa
+#*
+#****************************************************************************
+
+COMMON_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+PACKAGES_DIR := $(abspath $(COMMON_DIR)/../../packages)
+
+clean ::
+	rm -rf work transcript modelsim.ini
diff --git a/dv/bringup/bringup_tb.sv b/dv/common/sv/fwpayload_tb.sv
similarity index 90%
rename from dv/bringup/bringup_tb.sv
rename to dv/common/sv/fwpayload_tb.sv
index 39e33f6..10266e3 100644
--- a/dv/bringup/bringup_tb.sv
+++ b/dv/common/sv/fwpayload_tb.sv
@@ -1,5 +1,5 @@
 /****************************************************************************
- * bringup_tb.sv
+ * fwpayload_tb.sv
  ****************************************************************************/
 `ifdef IVERILOG
 `timescale 1ns/1ns
@@ -9,11 +9,11 @@
 	`define MPRJ_IO_PADS 38
 `endif
 /**
- * Module: bringup_tb
+ * Module: fwpayload_tb
  * 
  * TODO: Add module documentation
  */
-module bringup_tb(input clk);
+module fwpayload_tb(input clk);
 	
 `ifdef HAVE_HDL_CLOCKGEN
 	reg clk_r = 0;
@@ -33,7 +33,7 @@
 		initial begin
 			if ($test$plusargs("dumpvars")) begin
 				$dumpfile("simx.vcd");
-				$dumpvars(0, bringup_tb);
+				$dumpvars(0, fwpayload_tb);
 			end
 			if (!$value$plusargs("timeout=%d", timeout)) begin
 				timeout=1000;
@@ -78,14 +78,14 @@
 		) u_wb (
 			.clock(wb_clk_i),
 			.reset(wb_rst_i),
-			.stb_o(wbs_stb_i),
-			.cyc_o(wbs_cyc_i),
-			.we_o(wbs_we_i),
-			.sel_o(wbs_sel_i),
-			.dat_o(wbs_dat_i),
-			.adr_o(wbs_adr_i),
-			.ack_i(wbs_ack_o),
-			.dat_i(wbs_dat_o)
+			.stb(wbs_stb_i),
+			.cyc(wbs_cyc_i),
+			.we(wbs_we_i),
+			.sel(wbs_sel_i),
+			.dat_w(wbs_dat_i),
+			.adr(wbs_adr_i),
+			.ack(wbs_ack_o),
+			.dat_r(wbs_dat_o)
 		);
 	
 	wire [127:0] la_data_in;
@@ -110,7 +110,7 @@
 	
 	wire   user_clock2 = clock;
 
-	user_project_wrapper u_dut(
+	fwpayload u_dut(
 			.vdda1(vdda1),	// User area 1 3.3V supply
 			.vdda2(vdda2),	// User area 2 3.3V supply
 			.vssa1(vssa1),	// User area 1 analog ground
@@ -140,8 +140,9 @@
 			// IOs
 			.io_in(io_in),
 			.io_out(io_out),
-			.io_oeb(io_oeb),
+			.io_oeb(io_oeb)
 
+			/*
 			// Analog (direct connection to GPIO pad---use with caution)
 			// Note that analog I/O is not available on the 7 lowest-numbered
 			// GPIO pads, and so the analog_io indexing is offset from the
@@ -150,6 +151,7 @@
 
 			// Independent clock (on independent integer divider)
 			.user_clock2(user_clock2)
+			 */
 			);
 
 endmodule
diff --git a/dv/common/vlsim.mk b/dv/common/vlsim.mk
index 20c6e77..f48611b 100644
--- a/dv/common/vlsim.mk
+++ b/dv/common/vlsim.mk
@@ -16,6 +16,8 @@
 #****************************************************************************
 
 COMMON_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+
+ifneq (1,$(RULES))
 PACKAGES_DIR := $(abspath $(COMMON_DIR)/../../packages)
 VLSIM := $(PACKAGES_DIR)/python/bin/vlsim
 PYBFMS_DPI_LIB := $(shell $(PACKAGES_DIR)/python/bin/pybfms lib)
@@ -40,6 +42,8 @@
 DPI_LIBS += $(PYBFMS_DPI_LIB)
 VPI_LIBS += $(COCOTB_PREFIX)/cocotb/libs/libcocotbvpi_verilator.so
 
+else # Rules
+
 build : $(SIMV)
 
 $(SIMV) : $(SRCS) pybfms_gen.sv pybfms_gen.c
@@ -53,6 +57,4 @@
 	$(PACKAGES_DIR)/python/bin/pybfms generate \
 		-l sv $(foreach m,$(PYBFMS_MODULES),-m $(m)) -o $@
 
-clean ::
-	rm -f simv.* simx.fst simx.vcd pybfms_gen.sv pybfms_gen.c
-	rm -rf obj_dir
+endif
diff --git a/dv/common/vlsim_clean.mk b/dv/common/vlsim_clean.mk
new file mode 100644
index 0000000..689c6d6
--- /dev/null
+++ b/dv/common/vlsim_clean.mk
@@ -0,0 +1,10 @@
+#****************************************************************************
+#* vlsim_clean.mk
+#*
+#* Clean target for Verilotor Vlsim
+#*
+#****************************************************************************
+
+clean ::
+	rm -f simv.* simx.fst simx.vcd pybfms_gen.sv pybfms_gen.c
+	rm -rf obj_dir
diff --git a/dv/mgmt_mem_access/Makefile b/dv/mgmt_mem_access/Makefile
new file mode 100644
index 0000000..0130c3e
--- /dev/null
+++ b/dv/mgmt_mem_access/Makefile
@@ -0,0 +1,21 @@
+TEST_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+
+include $(TEST_DIR)/../common/defs_rules.mk
+
+#********************************************************************
+#* cocotb testbench setup
+#********************************************************************
+PYTHONPATH := $(TEST_DIR):$(PYTHONPATH)
+export PYTHONPATH
+
+MODULE=fwpayload_tests.mgmt_mem_access
+export MODULE
+
+VLSIM_OPTIONS += -Wno-fatal --autoflush
+
+RULES := 1
+
+all : run
+
+include $(TEST_DIR)/../common/defs_rules.mk
+
diff --git a/etc/packages.mf b/etc/packages.mf
index 482dac7..e0873b1 100644
--- a/etc/packages.mf
+++ b/etc/packages.mf
@@ -1,4 +1,4 @@
 
 fwrisc@https://github.com/mballance/fwrisc.git
-wb_sys_ip@https://github.com/mballance/wb_sys_ip.git
+fw-wishbone-interconnect@https://github.com/featherweight-ip/fw-wishbone-interconnect.git
 
diff --git a/openlane/Makefile b/openlane/Makefile
new file mode 100644
index 0000000..bbfd5f9
--- /dev/null
+++ b/openlane/Makefile
@@ -0,0 +1,63 @@
+#****************************************************************************
+#* 
+#****************************************************************************
+BLOCKS = $(shell find * -maxdepth 0 -type d)
+CONFIG = $(foreach block,$(BLOCKS), ./$(block)/config.tcl)
+CLEAN = $(foreach block,$(BLOCKS), clean-$(block))
+
+OPENLANE_IMAGE_NAME ?= openlane:rc4
+OPENLANE_BASIC_COMMAND = "cd /project/openlane && flow.tcl -design ./$* -save_path .. -save -tag $* -overwrite"
+OPENLANE_INTERACTIVE_COMMAND = "cd /project/openlane && flow.tcl -it -file ./$*/interactive.tcl"
+
+ifeq (,$(shell which podman))
+# Podman (CentOS8+) doesn't require user-spec options
+USER_PERM_OPTIONS=-u $(shell id -u $(USER)):$(shell id -g $(USER))
+endif
+
+all: $(BLOCKS)
+
+$(CONFIG) :
+	@echo "Missing $@. Please create a configuration for that design"
+	@exit 1
+
+$(BLOCKS) : % : ./%/config.tcl FORCE
+ifeq ($(OPENLANE_ROOT),)
+	@echo "Please export OPENLANE_ROOT"
+	@exit 1
+endif
+ifeq ($(PDK_ROOT),)
+	@echo "Please export PDK_ROOT"
+	@exit 1
+endif
+	@echo "###############################################"
+	@sleep 1
+
+	@if [[ -f ./$*/interactive.tcl ]]; then\
+		docker run -it -v $(OPENLANE_ROOT):/openLANE_flow \
+		-v $(PDK_ROOT):$(PDK_ROOT) \
+		-v $(PWD)/..:/project \
+		-e PDK_ROOT=$(PDK_ROOT) \
+		$(USER_PERM_OPTIONS) \
+		$(OPENLANE_IMAGE_NAME) sh -c $(OPENLANE_INTERACTIVE_COMMAND);\
+	else\
+		docker run -it -v $(OPENLANE_ROOT):/openLANE_flow \
+		-v $(PDK_ROOT):$(PDK_ROOT) \
+		-v $(PWD)/..:/project \
+		-e PDK_ROOT=$(PDK_ROOT) \
+		$(USER_PERM_OPTIONS) \
+		$(OPENLANE_IMAGE_NAME) sh -c $(OPENLANE_BASIC_COMMAND);\
+	fi
+
+
+FORCE:
+
+clean: 
+	@echo "Use clean_all to clean everything :)"
+
+clean_all: $(CLEAN)
+
+$(CLEAN): clean-% :
+	rm -rf runs/$*
+	rm -rf ../gds/$**
+	rm -rf ../mag/$**
+	rm -rf ../lef/$**
diff --git a/openlane/fwpayload/config.tcl b/openlane/fwpayload/config.tcl
new file mode 100644
index 0000000..6b7dcb5
--- /dev/null
+++ b/openlane/fwpayload/config.tcl
@@ -0,0 +1,69 @@
+set script_dir [file dirname [file normalize [info script]]]
+
+set ::env(DESIGN_NAME) user_project_wrapper
+set ::env(FP_PIN_ORDER_CFG) $script_dir/pin_order.cfg
+
+#set ::env(CLOCK_PORT) "user_clock2"
+set ::env(CLOCK_PORT) "wb_clk_i"
+set ::env(CLOCK_NET) "mprj.clk"
+#set ::env(CLOCK_NET) "wb_clk_i"
+
+set ::env(CLOCK_PERIOD) "10"
+
+set ::env(FP_SIZING) absolute
+#set ::env(DIE_AREA) "0 0 2700 3700"
+set ::env(DIE_AREA) "0 0 2000 2000"
+# Default density is 0.4
+set ::env(PL_TARGET_DENSITY) 0.15
+# set ::env(PL_TARGET_DENSITY) 0.85
+set ::env(PL_OPENPHYSYN_OPTIMIZATIONS) 0
+
+set ::env(PL_SKIP_INITIAL_PLACEMENT) 1
+
+set vlog_files ""
+lappend vlog_files "$script_dir/../../verilog/rtl/user_project_wrapper.v"
+lappend vlog_files "$script_dir/../../verilog/rtl/fwpayload.v"
+lappend vlog_files "$script_dir/../../verilog/rtl/spram_32x256.sv"
+lappend vlog_files "$script_dir/../../verilog/rtl/spram.v"
+lappend vlog_files "$script_dir/../../verilog/rtl/simple_spi_master.v"
+lappend vlog_files "$script_dir/../../verilog/rtl/simpleuart.v"
+lappend vlog_files "$script_dir/../../packages/fw-wishbone-interconnect/verilog/rtl/wb_interconnect_NxN.v"
+lappend vlog_files "$script_dir/../../packages/fw-wishbone-interconnect/verilog/rtl/wb_interconnect_arb.v"
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_wb.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_alu.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_c_decode.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_decode.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_exec.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_fetch.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_mem.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_mul_div_shift.sv
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_regfile.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_rv32i.sv 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_rv32i_wb.sv 
+#lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_rv32im.sv 
+#lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_rv32imc.sv 
+#lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_system_op.svh 
+lappend vlog_files $script_dir/../../packages/fwrisc/rtl/fwrisc_tracer.sv
+#foreach path [glob -directory "$script_dir/../../packages/fwrisc/rtl" "*.sv*"] {
+#	lappend vlog_files $path
+#}
+
+puts "vlog_files=$vlog_files"
+
+set ::env(VERILOG_INCLUDE_DIRS) "\
+        $script_dir/../../packages/fwrisc/rtl \
+        $script_dir/../../packages/fwprotocol-defs/src/sv"
+set ::env(VERILOG_FILES) "$vlog_files"
+#set ::env(VERILOG_FILES) "\
+#	$script_dir/../../rtl/user_project_wrapper.v \
+#	$script_dir/../../rtl/fwpayload.v"
+
+#set ::env(VERILOG_FILES_BLACKBOX) "\
+#	$script_dir/../../rtl/fwpayload.v"
+
+#set ::env(EXTRA_LEFS) "\
+#	$script_dir/../../lef/user_proj_example.lef"
+
+#set ::env(EXTRA_GDS_FILES) "\
+#	$script_dir/../../gds/user_proj_example.gds"
diff --git a/verilog/rtl/fwpayload.v b/verilog/rtl/fwpayload.v
index da198ef..23ec28a 100644
--- a/verilog/rtl/fwpayload.v
+++ b/verilog/rtl/fwpayload.v
@@ -2,6 +2,7 @@
 /****************************************************************************
  * fwpayload.v
  ****************************************************************************/
+`include "wishbone_macros.svh"
 
 `ifndef MPRJ_IO_PADS
 	`define MPRJ_IO_PADS 38
@@ -47,45 +48,33 @@
 		output [`MPRJ_IO_PADS-1:0] 		io_oeb
 		);
 	
-	wire clk, rst;
+	wire clk, rst, core_rst;
 
+	/****************************************************************
+	 * Interconnect definitions
+	 ****************************************************************/
 	// System interconnect
 	localparam N_INITIATORS = 3;
 	localparam INIT_ID_CORE_I = 0;
 	localparam INIT_ID_CORE_D = 1;
 	localparam INIT_ID_MGMT   = 2;
+	`WB_WIRES_ARR(i_ic_,32,32,N_INITIATORS);
 	
-	localparam N_TARGETS = 1;
+	localparam N_TARGETS = 4;
 	localparam TGT_ID_SRAM = 0;
-	// TBD
 	localparam TGT_ID_SPI  = 1;
 	localparam TGT_ID_UART = 2;
 	localparam TGT_ID_GPIO = 3;
-	wire[32*N_INITIATORS-1:0]		IC_I_ADR;
-	wire[32*N_INITIATORS-1:0]		IC_I_DAT_W;
-	wire[32*N_INITIATORS-1:0]		IC_I_DAT_R;
-	wire[N_INITIATORS-1:0]			IC_I_CYC;
-	wire[N_INITIATORS-1:0]			IC_I_ERR;
-	wire[4*N_INITIATORS-1:0]		IC_I_SEL;
-	wire[N_INITIATORS-1:0]			IC_I_STB;
-	wire[N_INITIATORS-1:0]			IC_I_ACK;
-	wire[N_INITIATORS-1:0]			IC_I_WE;
+	`WB_WIRES_ARR(ic_t_,32,32,N_TARGETS);
 	
-	wire[32*(N_TARGETS+1)-1:0]		IC_T_ADR;
-	wire[32*(N_TARGETS+1)-1:0]		IC_T_DAT_W;
-	wire[32*(N_TARGETS+1)-1:0]		IC_T_DAT_R;
-	wire[N_TARGETS:0]				IC_T_CYC;
-	wire[N_TARGETS:0]				IC_T_ERR;
-	wire[4*(N_TARGETS+1)-1:0]		IC_T_SEL;
-	wire[N_TARGETS:0]				IC_T_STB;
-	wire[N_TARGETS:0]				IC_T_ACK;
-	wire[N_TARGETS:0]				IC_T_WE;
-
-	// Interconnect has a default target that
-	// to which unmapped accesses are directed
-	assign IC_T_ACK[N_TARGETS] = 1;
-	assign IC_T_ERR[N_TARGETS] = 1;
-	assign IC_T_DAT_R[32*N_TARGETS+:32] = 0;
+	// Memory map
+	//
+	// 28-bit address space, with the upper 4 bits masked
+	//
+	// 0x00000000..0x00000FFFF - Program/data memory
+	// 0x01000000..0x010000000 - UART
+	// 0x01000000..0x010000100 - SPI
+	// 0x01000000..0x010000200 - GPIO
 	
 	// Interconnect
 	wb_interconnect_NxN #(
@@ -94,147 +83,95 @@
 			.N_INITIATORS(N_INITIATORS),
 			.N_TARGETS(N_TARGETS),
 			.I_ADR_MASK({
-				{ 8'hFF, {24{1'b0}} }
+				{ 32'h0F00_0000    },
+				{ 32'h0FFF_FF00    },
+				{ 32'h0FFF_FF00    },
+				{ 32'h0FFF_FF00    }
 				}),
 			.T_ADR({
-				{ 32'h8000_0000 }
+				{ 32'h0000_0000 },
+				{ 32'h0100_0000 },
+				{ 32'h0100_0100 },
+				{ 32'h0100_0200 }
 				})
 		) u_ic (
-			.clk(clk),
-			.rst(rst),
-			.ADR(IC_I_ADR),
-			.DAT_W(IC_I_DAT_W),
-			.DAT_R(IC_I_DAT_R),
-			.CYC(IC_I_CYC),
-			.ERR(IC_I_ERR),
-			.SEL(IC_I_SEL),
-			.STB(IC_I_STB),
-			.ACK(IC_I_ACK),
-			.WE(IC_I_WE),
-			
-			.TADR(IC_T_ADR),
-			.TDAT_W(IC_T_DAT_W),
-			.TDAT_R(IC_T_DAT_R),
-			.TCYC(IC_T_CYC),
-			.TERR(IC_T_ERR),
-			.TSEL(IC_T_SEL),
-			.TSTB(IC_T_STB),
-			.TACK(IC_T_ACK),
-			.TWE(IC_T_WE)
+			.clock(clk),
+			.reset(rst),
+		
+			`WB_CONNECT(,i_ic_),
+			`WB_CONNECT(t,ic_t_)
 		);
 
 	/****************************************************************
 	 * Connect management interface to port 1 on the interconnect
 	 ****************************************************************/
-	assign IC_I_ADR[32*INIT_ID_MGMT+:32] = wbs_adr_i;
-	assign IC_I_DAT_W[32*INIT_ID_MGMT+:32] = wbs_dat_i;
-	assign wbs_dat_o = IC_I_DAT_R[32*INIT_ID_MGMT+:32];
-	assign IC_I_CYC[INIT_ID_MGMT] = wbs_cyc_i;
-//	assign IC_I_ERR[INIT_ID_MGMT] = //wbs_cyc_i;
-	assign IC_I_SEL[4*INIT_ID_MGMT+:4] = wbs_sel_i;
-	assign IC_I_STB[INIT_ID_MGMT] = wbs_stb_i;
-	assign wbs_ack_o = IC_I_ACK[INIT_ID_MGMT];
-	assign IC_I_WE[INIT_ID_MGMT] = wbs_we_i;
+	assign i_ic_adr[32*INIT_ID_MGMT+:32] = wbs_adr_i;
+	assign i_ic_dat_w[32*INIT_ID_MGMT+:32] = wbs_dat_i;
+	assign wbs_dat_o = i_ic_dat_r[32*INIT_ID_MGMT+:32];
+	assign i_ic_cyc[INIT_ID_MGMT] = wbs_cyc_i;
+	assign i_ic_sel[4*INIT_ID_MGMT+:4] = wbs_sel_i;
+	assign i_ic_stb[INIT_ID_MGMT] = wbs_stb_i;
+	assign wbs_ack_o = i_ic_ack[INIT_ID_MGMT];
+	assign i_ic_we[INIT_ID_MGMT] = wbs_we_i;
 	
 	// Clock/reset control
 	// Allow the logic analyzer to take control of clock/reset
-	// Default to using the caravel clock/reset
 	assign clk = (~la_oen[127]) ? la_data_in[127]: wb_clk_i;
 	assign rst = (~la_oen[126]) ? ~la_data_in[126]: wb_rst_i;
 	assign core_rst = (~la_oen[125]) ? ~la_data_in[125]: wb_rst_i;
-//	assign clk = wb_clk_i;
-//	assign rst = wb_rst_i;
 	
-	localparam RAM_BITS = 8;
-	localparam ROM_BITS = 8;
-
+	/****************************************************************
+	 * FWRISC instance
+	 ****************************************************************/
 	fwrisc_rv32i_wb u_core (
 				.clock(clk),
 				.reset(core_rst),
 
-				.wbi_adr_o(IC_I_ADR[32*INIT_ID_CORE_I+:32]),
-				.wbi_dat_o(IC_I_DAT_W[32*INIT_ID_CORE_I+:32]),
-				.wbi_dat_i(IC_I_DAT_R[32*INIT_ID_CORE_I+:32]),
-				.wbi_cyc_o(IC_I_CYC[INIT_ID_CORE_I]),
-				.wbi_err_i(IC_I_ERR[INIT_ID_CORE_I]),
-				.wbi_sel_o(IC_I_SEL[4*INIT_ID_CORE_I+:4]),
-				.wbi_stb_o(IC_I_STB[INIT_ID_CORE_I]),
-				.wbi_ack_i(IC_I_ACK[INIT_ID_CORE_I]),
-				.wbi_we_o(IC_I_WE[INIT_ID_CORE_I]),
-				
-				.wbd_adr_o(IC_I_ADR[32*INIT_ID_CORE_D+:32]),
-				.wbd_dat_o(IC_I_DAT_W[32*INIT_ID_CORE_D+:32]),
-				.wbd_dat_i(IC_I_DAT_R[32*INIT_ID_CORE_D+:32]),
-				.wbd_cyc_o(IC_I_CYC[INIT_ID_CORE_D]),
-				.wbd_err_i(IC_I_ERR[INIT_ID_CORE_D]),
-				.wbd_sel_o(IC_I_SEL[4*INIT_ID_CORE_D+:4]),
-				.wbd_stb_o(IC_I_STB[INIT_ID_CORE_D]),
-				.wbd_ack_i(IC_I_ACK[INIT_ID_CORE_D]),
-				.wbd_we_o(IC_I_WE[INIT_ID_CORE_D])
+				`WB_CONNECT_ARR(wbi_,i_ic_,INIT_ID_CORE_I,32,32),
+				`WB_CONNECT_ARR(wbd_,i_ic_,INIT_ID_CORE_D,32,32)
 			);
 
-	
 	// Probes
 	// - PC 
 	//   - [31:0] input
-	// - Regs
-	//   - [63:32] input  - registers (via mux)
-	//   - [68:64] output - select
+	// - instr_complete
+	//   - [32]   input
+	// - gpio_out
+	//   - [39:36] input
+	// - Clock and reset
 	//   - 127 output     - clock
 	//   - 126 output     - reset
-	localparam IVALID_OFF        = 65;
-	localparam REG_PROBE_SEL_OFF = 64;
-	localparam REG_PROBE_OFF     = 32;
-	localparam PC_PROBE_OFF      = 0;
-	wire[4:0]             reg_probe_sel = (
-			la_oen[REG_PROBE_SEL_OFF+4:REG_PROBE_SEL_OFF] == 5'b0000)?
-			la_data_in[REG_PROBE_SEL_OFF+4:REG_PROBE_SEL_OFF]:5'b0000;
-	wire[31:0]            reg_probe;
-	wire[31:0]            pc_probe;
+	//   - 125 output     - core_reset
+	localparam LA_CLOCK					= 127;
+	localparam LA_RESET_SYS				= 126;
+	localparam LA_RESET_CORE			= 125;
+	localparam LA_GPIO_IN				= 40;
+	localparam LA_GPIO_OUT				= 36;
+	localparam LA_UART_RX				= 34;
+	localparam LA_UART_TX				= 33;
+	localparam LA_INSTR_COMPLETE        = 32;
+	localparam LA_PC      				= 0;
 	
-	assign la_data_out[REG_PROBE_OFF+31:REG_PROBE_OFF] = reg_probe;
-	assign la_data_out[PC_PROBE_OFF+31:PC_PROBE_OFF] = pc_probe;
-//	assign la_data_out[IVALID_OFF] = u_core.u_core.instr_complete;
+	wire[31:0]      pc_probe = u_core.u_core.u_core.pc;
+	assign la_data_out[LA_PC+:32] = pc_probe;
+	assign la_data_out[LA_INSTR_COMPLETE] = u_core.u_core.u_core.instr_complete;
 
-	// 640 pixels
-	// 16x16?
-	// - Each block is 40p wide
-	// -
-	// - 40ns per pix, 1600ns per block
-	// TODO: dedicated reset for core, to allow us to isolate it from the system
-	// Video shift register
-	// - Need two levels
-	// - Output
-	// - Ready
-	// - Need clock divider to control shift rate
-	// - Need IRQ to signal empty
-
-	// Small memory (1KB ok?)
-	// - Must be dual-port with access from slave port
-	// ROM: 'h8000_0000
-	// RAM: 'h8000_8000
-	// LED: 'hC000_0000
-	
-//	initial begin
-//		$readmemh("rom.hex", rom);
-//	end
-	
 	/****************************************************************
 	 * Simple WB to SRAM bridge
 	 ****************************************************************/
 	reg[1:0] wb_bridge_state = 0;
-	wire[31:0] sram_adr_i = IC_T_ADR[32*TGT_ID_SRAM+:32];
-	wire[31:0] sram_dat_w = IC_T_DAT_W[32*TGT_ID_SRAM+:32];
+	wire[31:0] sram_adr_i = ic_t_adr[32*TGT_ID_SRAM+:32];
+	wire[31:0] sram_dat_w = ic_t_dat_w[32*TGT_ID_SRAM+:32];
 	wire[31:0] sram_dat_r;
-	assign IC_T_DAT_R[32*TGT_ID_SRAM+:32] = sram_dat_r;
-	wire       sram_cyc_i = IC_T_CYC[TGT_ID_SRAM];
-	assign     IC_T_ERR[TGT_ID_SRAM] = 0;
-	wire[3:0]  sram_sel_i = IC_T_SEL[4*TGT_ID_SRAM+:4];
-	wire       sram_stb_i = IC_T_STB[TGT_ID_SRAM];
+	assign ic_t_dat_r[32*TGT_ID_SRAM+:32] = sram_dat_r;
+	wire       sram_cyc_i = ic_t_cyc[TGT_ID_SRAM];
+	assign     ic_t_err[TGT_ID_SRAM] = 0;
+	wire[3:0]  sram_sel_i = ic_t_sel[4*TGT_ID_SRAM+:4];
+	wire       sram_stb_i = ic_t_stb[TGT_ID_SRAM];
 	wire       sram_ack_o;
-	assign     IC_T_ACK[TGT_ID_SRAM] = sram_ack_o;
-	wire       sram_we_i  = IC_T_WE[TGT_ID_SRAM];
-
+	assign     ic_t_ack[TGT_ID_SRAM] = sram_ack_o;
+	wire       sram_we_i  = ic_t_we[TGT_ID_SRAM];
+	
 	always @(posedge wb_clk_i) begin
 		if (rst == 1) begin
 			wb_bridge_state <= 0;
@@ -268,14 +205,113 @@
 			.a_sel(sram_sel_i));
 	assign sram_ack_o = (wb_bridge_state == 3);
 	
-	// Some form of general I/O
-	// - GPIO?
-	// - 
+	/****************************************************************
+	 * External interfaces
+	 ****************************************************************/
 	
-	// Some form of specific I/O
 	// - UART
-	// - SPI
+	wire uart_enabled;
+	wire ser_tx;
+	wire ser_rx;
+	simpleuart_wb #(
+			.BASE_ADR(32'h0000_0000)
+		) u_uart (
+			.wb_clk_i(clk),
+			.wb_rst_i(rst),
+			.wb_adr_i({24'b0, ic_t_adr[32*TGT_ID_UART+:8]}),
+			.wb_dat_i(ic_t_dat_w[32*TGT_ID_UART+:32]),
+			.wb_sel_i(ic_t_sel[4*TGT_ID_UART+:4]),
+			.wb_we_i(ic_t_we[TGT_ID_UART]),
+			.wb_cyc_i(ic_t_cyc[TGT_ID_UART]),
+			.wb_stb_i(ic_t_stb[TGT_ID_UART]),
+			.wb_ack_o(ic_t_ack[TGT_ID_UART]),
+			.wb_dat_o(ic_t_dat_r[32*TGT_ID_UART+:32]),
+			
+			.uart_enabled(uart_enabled),
+			.ser_tx(ser_tx),
+			.ser_rx(ser_rx)
+		);
 	
+	// - SPI
+	wire hk_connect;
+	wire sdi;
+	wire csb;
+	wire sck;
+	wire sdo;
+	wire sdoenb;
+	wire irq;
+	simple_spi_master_wb #(
+			.BASE_ADR(32'h0000_0000)
+		) u_spi (
+			.wb_clk_i(clk),
+			.wb_rst_i(rst),
+			.wb_adr_i({24'b0, ic_t_adr[32*TGT_ID_SPI+:8]}),
+			.wb_dat_i(ic_t_dat_w[32*TGT_ID_SPI+:32]),
+			.wb_sel_i(ic_t_sel[4*TGT_ID_SPI+:4]),
+			.wb_we_i(ic_t_we[TGT_ID_SPI]),
+			.wb_cyc_i(ic_t_cyc[TGT_ID_SPI]),
+			.wb_stb_i(ic_t_stb[TGT_ID_SPI]),
+			.wb_ack_o(ic_t_ack[TGT_ID_SPI]),
+			.wb_dat_o(ic_t_dat_r[32*TGT_ID_SPI+:32]),
+			
+			.hk_connect(hk_connect),
+			.sdi(sdi),
+			.csb(csb),
+			.sck(sck),
+			.sdo(sdo),
+			.sdoenb(sdoenb),
+			.irq(irq)
+		);
+	
+	// - Simple GPIO
+	reg[3:0]	gpio_out;
+	wire[3:0]	gpio_in;
+	
+	wire[31:0] gpio_adr_i = ic_t_adr[32*TGT_ID_GPIO+:32];
+	wire[31:0] gpio_dat_w = ic_t_dat_w[32*TGT_ID_GPIO+:32];
+	wire[31:0] gpio_dat_r = {20'b0, gpio_in, 4'b0, gpio_out};
+	assign ic_t_dat_r[32*TGT_ID_GPIO+:32] = gpio_dat_r;
+	wire       gpio_cyc_i = ic_t_cyc[TGT_ID_GPIO];
+	assign     ic_t_err[TGT_ID_GPIO] = 0;
+	wire[3:0]  gpio_sel_i = ic_t_sel[4*TGT_ID_GPIO+:4];
+	wire       gpio_stb_i = ic_t_stb[TGT_ID_GPIO];
+	reg        gpio_ack_o;
+	assign     ic_t_ack[TGT_ID_GPIO] = gpio_ack_o;
+	wire       gpio_we_i  = ic_t_we[TGT_ID_GPIO];
+	
+	always @(posedge wb_clk_i) begin
+		if (rst == 1) begin
+			gpio_ack_o <= 1'b0;
+			gpio_out <= 4'b0;
+		end else begin
+			gpio_ack_o <= (gpio_cyc_i && gpio_stb_i);
+			
+			if (gpio_cyc_i && gpio_stb_i && gpio_we_i) begin
+				gpio_out <= gpio_dat_w[3:0];
+			end
+		end
+	end	
+	
+	
+	/****************************************************************
+	 * Outputs
+	 ****************************************************************/
+	// UART
+	assign io_out[16] = ser_tx;
+	assign ser_rx = io_in[17];
+	
+	assign sdi = io_in[18];
+	assign io_out[19] = csb;
+	assign io_out[20] = sck;
+	assign io_out[21] = sdo;
+	assign io_out[22] = sdoenb;
+
+	// GPIO
+	assign io_out[26:23] = gpio_out;
+	assign gpio_in = io_in[30:27];
+	
+	// Probe the GPIO output with the LA
+	assign la_data_in[39:36] = gpio_out;
 	
 endmodule
 
diff --git a/verilog/rtl/simple_spi_master.v b/verilog/rtl/simple_spi_master.v
index 4cf8b46..c4bbd4d 100755
--- a/verilog/rtl/simple_spi_master.v
+++ b/verilog/rtl/simple_spi_master.v
@@ -146,7 +146,7 @@
     output [31:0] reg_dat_do,
     output	  reg_dat_wait,
     output	  irq_out,
-    output	  err_out,
+    output reg	  err_out,
 
     output	 hk_connect,	// Connect to housekeeping SPI
     input 	 sdi,	 // SPI input
@@ -164,7 +164,6 @@
     reg 	  isdo, hsck, icsb;
     reg [1:0] state;
     reg 	  isck;
-    reg	  err_out;
  
     reg [7:0]  treg, rreg, d_latched;
     reg [2:0]  nbit;
@@ -180,13 +179,6 @@
     reg	   enable;
     reg	   hkconn;
  
-    wire	  csb;
-    wire	  irq_out;
-    wire	  sck;
-    wire	  sdo;
-    wire	  sdoenb;
-    wire	  hk_connect;
-
     // Define behavior for inverted SCK and inverted CSB
     assign    	  csb = (enable == 1'b0) ? 1'bz : (invcsb) ? ~icsb : icsb;
     assign	  sck = (enable == 1'b0) ? 1'bz : (invsck) ? ~isck : isck;
diff --git a/verilog/rtl/simpleuart.v b/verilog/rtl/simpleuart.v
index 96b3b4e..055e1c7 100644
--- a/verilog/rtl/simpleuart.v
+++ b/verilog/rtl/simpleuart.v
@@ -95,7 +95,7 @@
     input clk,
     input resetn,
 
-    output enabled,
+    output reg enabled,
     output ser_tx,
     input  ser_rx,
 
@@ -114,7 +114,6 @@
     output        reg_dat_wait
 );
     reg [31:0] cfg_divider;
-    reg        enabled;
 
     reg [3:0] recv_state;
     reg [31:0] recv_divcnt;
diff --git a/verilog/rtl/spram_32x512.sv b/verilog/rtl/spram_32x512.sv
new file mode 100644
index 0000000..3be8195
--- /dev/null
+++ b/verilog/rtl/spram_32x512.sv
@@ -0,0 +1,31 @@
+/****************************************************************************
+ * spram_32x256.sv
+ ****************************************************************************/
+
+/**
+ * Module: spram_32x512
+ * 
+ * TODO: Add module documentation
+ */
+module spram_32x512(
+		input				clock,
+		input  [8-1:0]		a_adr,
+		input  [32-1:0]		a_dat_i,
+		output [32-1:0]		a_dat_o,
+		input				a_we,
+		input  [32/8-1:0]	a_sel);
+
+	spram_byte_en #(
+			.ADDR_BITS(9),
+			.DATA_BITS(32)
+			) u_sram (
+			.clock(clock),
+			.a_adr(a_adr),
+			.a_dat_i(a_dat_i),
+			.a_dat_o(a_dat_o),
+			.a_we(a_we),
+			.a_sel(a_sel));
+
+endmodule
+
+
diff --git a/verilog/rtl/wb_interconnect_NxN.sv b/verilog/rtl/wb_interconnect_NxN.sv
deleted file mode 100644
index f3b0ce3..0000000
--- a/verilog/rtl/wb_interconnect_NxN.sv
+++ /dev/null
@@ -1,329 +0,0 @@
-/****************************************************************************
- * ${NAME}.sv
- ****************************************************************************/
-
-/**
- * Module: wb_interconnect_NxN
- * 
- * TODO: Add module documentation
- */
-module wb_interconnect_NxN #(
-		parameter 									WB_ADDR_WIDTH=32,
-		parameter 									WB_DATA_WIDTH=32,
-		parameter 									N_INITIATORS=1,
-		parameter 									N_TARGETS=1,
-		parameter [N_INITIATORS*WB_ADDR_WIDTH-1:0] 	I_ADR_MASK = {
-			{8'hFF, {24{1'b0}} }
-		},
-		parameter [N_TARGETS*WB_ADDR_WIDTH-1:0] 		T_ADR = {
-			{ 32'h2800_0000 }
-		}
-		) (
-		input										clk,
-		input										rst,
-		input[WB_ADDR_WIDTH*N_INITIATORS-1:0]		ADR,
-		input[WB_DATA_WIDTH*N_INITIATORS-1:0]		DAT_W,
-		output[WB_DATA_WIDTH*N_INITIATORS-1:0]		DAT_R,
-		input[N_INITIATORS-1:0]						CYC,
-		output[N_INITIATORS-1:0]					ERR,
-		input[(WB_DATA_WIDTH/8)*N_INITIATORS-1:0]	SEL,
-		input[N_INITIATORS-1:0]						STB,
-		output[N_INITIATORS-1:0]					ACK,
-		input[N_INITIATORS-1:0]						WE,
-
-		output[WB_ADDR_WIDTH*(N_TARGETS+1)-1:0]		TADR,
-		output[WB_DATA_WIDTH*(N_TARGETS+1)-1:0]		TDAT_W,
-		input[WB_DATA_WIDTH*(N_TARGETS+1)-1:0]		TDAT_R,
-		output[N_TARGETS:0]							TCYC,
-		input[N_TARGETS:0]							TERR,
-		output[(WB_DATA_WIDTH/8)*(N_TARGETS+1)-1:0]	TSEL,
-		output[N_TARGETS:0]							TSTB,
-		input[N_TARGETS:0]							TACK,
-		output[N_TARGETS:0]							TWE
-		);
-	
-	localparam WB_DATA_MSB = (WB_DATA_WIDTH-1);
-	localparam N_INIT_ID_BITS = (N_INITIATORS>1)?$clog2(N_INITIATORS):1;
-	localparam N_TARG_ID_BITS = $clog2(N_TARGETS+1);
-	localparam NO_TARGET  = {(N_TARG_ID_BITS+1){1'b1}};
-	localparam NO_INITIATOR = {(N_INIT_ID_BITS+1){1'b1}};
-	
-	// Interface to the decode-fail target
-//	wb_if				TERR();
-
-	function reg[N_TARG_ID_BITS:0] addr2targ_id(
-		input reg[N_INIT_ID_BITS-1:0]		initiator,
-		input reg[WB_ADDR_WIDTH-1:0] 		addr
-		);
-		integer i;
-		begin
-		addr2targ_id = N_TARGETS;
-//		$display("addr2targ_id: 'h%08h 'h%08h", addr, ADDR_RANGES);
-		for (i=0; i<N_TARGETS; i+=1) begin
-//			$display("Address Range: %0d 'h%08h..'h%08h", i, 
-//					ADDR_RANGES[(WB_ADDR_WIDTH*(i+2)-1)-:WB_ADDR_WIDTH],
-//					ADDR_RANGES[(WB_ADDR_WIDTH*(i+1)-1)-:WB_ADDR_WIDTH]);
-//			$display("  %0d %0d", (WB_ADDR_WIDTH*(i+2)-1), (WB_ADDR_WIDTH*(i+1)-1));
-			if (
-					(addr&I_ADR_MASK[(WB_ADDR_WIDTH*(i+1))-1-:WB_ADDR_WIDTH]) == 
-					(T_ADR[(WB_ADDR_WIDTH*(i+1))-1-:WB_ADDR_WIDTH])) begin
-				$display("Address 'h%08h: range=%0d", addr, N_TARGETS-1);
-				addr2targ_id = N_TARGETS-1;
-			end
-		end
-		end
-	endfunction
-	
-// Read request state machine
-
-	// Initiator state machine
-	reg[2:0]							initiator_state[N_INITIATORS-1:0];
-	reg[N_TARG_ID_BITS:0]				initiator_selected_target[N_INITIATORS-1:0];
-	wire								initiator_gnt[N_TARGETS:0];
-	wire[$clog2(N_INITIATORS)-1:0]		initiator_gnt_id[N_TARGETS:0];
-	wire[N_INITIATORS-1:0]				initiator_target_req[N_TARGETS:0];
-	
-	generate
-		genvar m_i;
-		for (m_i=0; m_i<N_INITIATORS; m_i=m_i+1) begin : block_m_i
-			always @(posedge clk) begin
-				if (rst == 1) begin
-					initiator_state[m_i] <= 0;
-					initiator_selected_target[m_i] <= NO_TARGET;
-				end else begin
-					case (initiator_state[m_i])
-						0: begin
-							if (CYC[m_i] && STB[m_i]) begin
-								initiator_state[m_i] <= 1;
-								initiator_selected_target[m_i] <= addr2targ_id(
-										m_i, 
-										ADR[WB_ADDR_WIDTH*m_i+:WB_ADDR_WIDTH]
-										);
-//								$display("Master %0d => Slave %0d", m_i, addr2targ_id(m_i, ADR[m_i]));
-							end
-						end
-						
-						1: begin
-							// Wait for the addressed target to acknowledge
-							if (CYC[m_i] && STB[m_i] && ACK[m_i]) begin
-								initiator_state[m_i] <= 0;
-								initiator_selected_target[m_i] <= NO_TARGET;
-							end
-						end
-					endcase
-				end
-			end
-		end
-	endgenerate
-
-	// Build the req vector for each target
-	generate
-		genvar m_req_i, m_req_j;
-
-		for (m_req_i=0; m_req_i <(N_TARGETS+1); m_req_i=m_req_i+1) begin : block_m_req_i
-			for (m_req_j=0; m_req_j < N_INITIATORS; m_req_j=m_req_j+1) begin : block_m_req_j
-				assign initiator_target_req[m_req_i][m_req_j] = (initiator_selected_target[m_req_j] == m_req_i);
-			end
-		end
-	endgenerate
-
-	generate
-		genvar s_arb_i;
-		
-		for (s_arb_i=0; s_arb_i<(N_TARGETS+1); s_arb_i=s_arb_i+1) begin : s_arb
-			wb_NxN_arbiter #(
-				.N_REQ  (N_INITIATORS)
-				) 
-				aw_arb (
-					.clk    (clk   ), 
-					.rst    (rst  ), 
-					.req    (initiator_target_req[s_arb_i]), 
-					.gnt    (initiator_gnt[s_arb_i]),
-					.gnt_id	(initiator_gnt_id[s_arb_i])
-				);
-		end
-	endgenerate
-
-	wire[N_INIT_ID_BITS:0]					target_active_initiator[N_TARGETS:0];
-
-	generate
-		genvar s_am_i;
-		
-		for (s_am_i=0; s_am_i<(N_TARGETS+1); s_am_i=s_am_i+1) begin : block_s_am_i
-			assign target_active_initiator[s_am_i] =
-				(initiator_gnt[s_am_i])?initiator_gnt_id[s_am_i]:NO_INITIATOR;
-		end
-	endgenerate
-	
-	// WB signals from target back to initiator
-	generate
-		genvar s2m_i;
-		
-		for (s2m_i=0; s2m_i<N_INITIATORS; s2m_i=s2m_i+1) begin : block_s2m_i
-			assign DAT_R[WB_DATA_WIDTH*s2m_i+:WB_DATA_WIDTH] = 
-				(initiator_selected_target[s2m_i] != NO_TARGET && 
-						initiator_gnt[initiator_selected_target[s2m_i]] && 
-						initiator_gnt_id[initiator_selected_target[s2m_i]] == s2m_i)?
-					TDAT_R[WB_DATA_WIDTH*initiator_selected_target[s2m_i]+:WB_DATA_WIDTH]:0;
-			assign ERR[s2m_i] = (initiator_selected_target[s2m_i] != NO_TARGET && 
-										initiator_gnt[initiator_selected_target[s2m_i]] && 
-										initiator_gnt_id[initiator_selected_target[s2m_i]] == s2m_i)?
-										TERR[initiator_selected_target[s2m_i]]:0;
-			assign ACK[s2m_i] = (initiator_selected_target[s2m_i] != NO_TARGET && 
-										initiator_gnt[initiator_selected_target[s2m_i]] && 
-										initiator_gnt_id[initiator_selected_target[s2m_i]] == s2m_i)?
-										TACK[initiator_selected_target[s2m_i]]:0;
-		end
-	endgenerate
-
-	// WB signals to target mux
-	generate
-		genvar m2s_i;
-		for(m2s_i=0; m2s_i<(N_TARGETS+1); m2s_i=m2s_i+1) begin : WB_M2S_assign
-			assign TADR[WB_ADDR_WIDTH*m2s_i+:WB_ADDR_WIDTH] = 
-				(target_active_initiator[m2s_i] == NO_INITIATOR)?0:ADR[WB_ADDR_WIDTH*target_active_initiator[m2s_i]+:WB_ADDR_WIDTH];
-			assign TDAT_W[WB_DATA_WIDTH*m2s_i+:WB_DATA_WIDTH] = 
-				(target_active_initiator[m2s_i] == NO_INITIATOR)?0:DAT_W[WB_DATA_WIDTH*target_active_initiator[m2s_i]+:WB_DATA_WIDTH];
-			assign TCYC[m2s_i] = (target_active_initiator[m2s_i] == NO_INITIATOR)?0:CYC[target_active_initiator[m2s_i]];
-			assign TSEL[(WB_DATA_WIDTH/8)*m2s_i+:(WB_DATA_WIDTH/8)] = 
-				(target_active_initiator[m2s_i] == NO_INITIATOR)?0:
-				SEL[(WB_DATA_WIDTH/8)*target_active_initiator[m2s_i]+:(WB_DATA_WIDTH/8)];
-			assign TSTB[m2s_i] = (target_active_initiator[m2s_i] == NO_INITIATOR)?0:STB[target_active_initiator[m2s_i]];
-			assign TWE[m2s_i] = (target_active_initiator[m2s_i] == NO_INITIATOR)?0:WE[target_active_initiator[m2s_i]];
-		end
-	endgenerate
-	
-	// Error target
-	reg err_req;
-	always @(posedge clk) begin
-		if (rst == 1) begin
-			err_req <= 0;
-		end else begin
-			if (TSTB[N_TARGETS] && TCYC[N_TARGETS] && !err_req) begin
-				err_req <= 1;
-			end else begin
-				err_req <= 0;
-			end
-		end
-	end
-endmodule
-
-module wb_NxN_arbiter #(
-		parameter 					N_REQ=2
-		) (
-		input						clk,
-		input						rst,
-		input[N_REQ-1:0]			req,
-		output						gnt,
-		output[$clog2(N_REQ)-1:0]	gnt_id
-		);
-	
-	reg state;
-	
-	reg [N_REQ-1:0]	gnt_o = 0;
-	reg [N_REQ-1:0]	last_gnt = 0;
-	reg [$clog2(N_REQ)-1:0] gnt_id_o = 0;
-	assign gnt = |gnt_o;
-	assign gnt_id = gnt_id_o;
-	
-	wire[N_REQ-1:0] gnt_ppc;
-	wire[N_REQ-1:0]	gnt_ppc_next;
-
-	generate
-		if (N_REQ > 1) begin
-			assign gnt_ppc_next = {gnt_ppc[N_REQ-2:0], 1'b0};
-		end else begin
-			assign gnt_ppc_next = gnt_ppc;
-		end
-	endgenerate
-
-	generate
-		genvar gnt_ppc_i;
-		
-	for (gnt_ppc_i=N_REQ-1; gnt_ppc_i>=0; gnt_ppc_i=gnt_ppc_i-1) begin : block_gnt_ppc_i
-		if (gnt_ppc_i == 0) begin
-			assign gnt_ppc[gnt_ppc_i] = last_gnt[0];
-		end else begin
-			assign gnt_ppc[gnt_ppc_i] = |last_gnt[gnt_ppc_i-1:0];
-		end
-	end
-	endgenerate
-	
-		wire[N_REQ-1:0]		unmasked_gnt;
-	generate
-		genvar unmasked_gnt_i;
-		
-	for (unmasked_gnt_i=0; unmasked_gnt_i<N_REQ; unmasked_gnt_i=unmasked_gnt_i+1) begin : block_unmasked_gnt_i
-		// Prioritized unmasked grant vector. Grant to the lowest active grant
-		if (unmasked_gnt_i == 0) begin
-			assign unmasked_gnt[unmasked_gnt_i] = req[unmasked_gnt_i];
-		end else begin
-			assign unmasked_gnt[unmasked_gnt_i] = (req[unmasked_gnt_i] & ~(|req[unmasked_gnt_i-1:0]));
-		end
-	end
-	endgenerate
-	
-		wire[N_REQ-1:0]		masked_gnt;
-	generate
-		genvar masked_gnt_i;
-		
-	for (masked_gnt_i=0; masked_gnt_i<N_REQ; masked_gnt_i=masked_gnt_i+1) begin : block_masked_gnt_i
-		if (masked_gnt_i == 0) begin
-			assign masked_gnt[masked_gnt_i] = (gnt_ppc_next[masked_gnt_i] & req[masked_gnt_i]);
-		end else begin
-			// Select first request above the last grant
-			assign masked_gnt[masked_gnt_i] = (gnt_ppc_next[masked_gnt_i] & req[masked_gnt_i] & 
-					~(|(gnt_ppc_next[masked_gnt_i-1:0] & req[masked_gnt_i-1:0])));
-		end
-	end
-	endgenerate
-	
-		wire[N_REQ-1:0] prioritized_gnt;
-
-	// Give priority to the 'next' request
-	assign prioritized_gnt = (|masked_gnt)?masked_gnt:unmasked_gnt;
-	
-	always @(posedge clk) begin
-		if (rst == 1) begin
-			state <= 0;
-			last_gnt <= 0;
-			gnt_o <= 0;
-			gnt_id_o <= 0;
-		end else begin
-			case (state) 
-				0: begin
-					if (|prioritized_gnt) begin
-						state <= 1;
-						gnt_o <= prioritized_gnt;
-						last_gnt <= prioritized_gnt;
-						gnt_id_o <= gnt2id(prioritized_gnt);
-					end
-				end
-				
-				1: begin
-					if ((gnt_o & req) == 0) begin
-						state <= 0;
-						gnt_o <= 0;
-					end
-				end
-			endcase
-		end
-	end
-
-	function reg[$clog2(N_REQ)-1:0] gnt2id(input reg[N_REQ-1:0] gnt);
-		integer i;
-		begin
-//		reg[$clog2(N_REQ)-1:0] result;
-		
-		gnt2id = 0;
-		
-		for (i=0; i<N_REQ; i++) begin
-			if (gnt[i]) begin
-				gnt2id |= i;
-			end
-		end
-	
-		end
-	endfunction
-
-endmodule