Moving toward a slightly larger system...

Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
diff --git a/.pydevproject b/.pydevproject
index 4684ce1..7f15358 100644
--- a/.pydevproject
+++ b/.pydevproject
@@ -5,5 +5,6 @@
     <pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
         <path>/${PROJECT_DIR_NAME}/packages/wishbone-bfms/src</path>
         <path>/${PROJECT_DIR_NAME}/dv/bringup/python</path>
+        <path>/${PROJECT_DIR_NAME}/packages/pybfms-logic-analyzer/src</path>
     </pydev_pathproperty>
 </pydev_project>
diff --git a/dv/bringup/Makefile b/dv/bringup/Makefile
index 990c880..ad7dbab 100644
--- a/dv/bringup/Makefile
+++ b/dv/bringup/Makefile
@@ -1,5 +1,5 @@
 BRINGUP_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
-RTL_DIR:=$(abspath $(BRINGUP_DIR)/../../rtl)
+RTL_DIR:=$(abspath $(BRINGUP_DIR)/../../verilog/rtl)
 PACKAGES_DIR:=$(abspath $(BRINGUP_DIR)/../../packages)
 FWRISC_RTL_DIR:=$(PACKAGES_DIR)/fwrisc/rtl
 
@@ -10,32 +10,44 @@
 INCDIRS += $(FWRISC_RTL_DIR)
 
 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 += $(RTL_DIR)/spram_32x256.sv
+SRCS += $(RTL_DIR)/spram.v
 SRCS += $(FWRISC_SRCS) 
 
+TOP_MODULE = bringup_tb
+
+TESTS=mgmt_mem_access
+
 #********************************************************************
 #* cocotb testbench setup
 #********************************************************************
-MODULE=bringup_tb
+MODULE=bringup_tests.$(TEST)
 export MODULE
 PYTHONPATH := $(BRINGUP_DIR)/python:$(PYTHONPATH)
 export PYTHONPATH
 PATH := $(PACKAGES_DIR)/python/bin:$(PATH)
 export PATH
 COCOTB_PREFIX := $(shell $(PACKAGES_DIR)/python/bin/cocotb-config --prefix)
-VPI_LIBS += $(COCOTB_PREFIX)/cocotb/libs/libcocotbvpi_verilator.so
+VPI_LIBS += $(COCOTB_PREFIX)/cocotb/libs/libcocotbvpi_modelsim.so
 
 
 VLSIM_CLKSPEC += -clkspec clk=10ns
-VLSIM_OPTIONS += -Wno-fatal --top-module bringup_tb
+VLSIM_OPTIONS += -Wno-fatal --top-module bringup_tb --autoflush
 SRCS += $(BRINGUP_DIR)/bringup_tb.sv
 
 SIM?=vlsim
 
-all : build run
+all : build 
+	for test in $(TESTS); do \
+		$(MAKE) -f $(BRINGUP_DIR)/Makefile run TEST=$${test}; \
+	done
 
 clean ::
 	echo "TODO"
+	
 
 include $(BRINGUP_DIR)/../common/$(SIM).mk
 
diff --git a/dv/bringup/bringup_tb.sv b/dv/bringup/bringup_tb.sv
index 8fd59a2..d28b0ee 100644
--- a/dv/bringup/bringup_tb.sv
+++ b/dv/bringup/bringup_tb.sv
@@ -17,9 +17,10 @@
 	assign clk_r = #5ns ~clk_r;
 `endif
 
-	wire clock = clk;
+	
+	wire clock = clk; 
 	reg[15:0]			reset_cnt;
-	reg[15:0]			reset_key;
+	reg[15:0]			reset_key /*verilator public*/;
 	
 	always @(posedge clock) begin
 		if (reset_key != 16'ha520) begin
@@ -44,9 +45,35 @@
 	wire wbs_ack_o;
 	wire [31:0] wbs_dat_o;
 	
+	wb_initiator_bfm #(
+			.ADDR_WIDTH(32),
+			.DATA_WIDTH(32)
+		) 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)
+		);
+	
 	wire [127:0] la_data_in;
 	wire [127:0] la_data_out;
-	wire [127:0] la_oen = 128'hFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFF;
+	wire [127:0] la_oen; //  = 128'hFFFF_FFFF_FFFF_FFFF__FFFF_FFFF_FFFF_FFFF;
+	
+	la_initiator_bfm #(
+			.WIDTH(128)
+		) u_la (
+			.clock(wb_clk_i),
+			.reset(wb_rst_i),
+			.data_in(la_data_out),
+			.data_out(la_data_in),
+			.oen(la_oen)
+		);
 	
 	wire [`MPRJ_IO_PADS-1:0] io_in;
 	wire [`MPRJ_IO_PADS-1:0] io_out;
diff --git a/dv/bringup/python/__pycache__/bringup_tb.cpython-36.pyc b/dv/bringup/python/__pycache__/bringup_tb.cpython-36.pyc
deleted file mode 100644
index f96b60b..0000000
--- a/dv/bringup/python/__pycache__/bringup_tb.cpython-36.pyc
+++ /dev/null
Binary files differ
diff --git a/dv/bringup/python/bringup_tb.py b/dv/bringup/python/bringup_tb.py
index 68e8135..774cffa 100644
--- a/dv/bringup/python/bringup_tb.py
+++ b/dv/bringup/python/bringup_tb.py
@@ -4,9 +4,28 @@
 @author: mballance
 '''
 import cocotb
+import wishbone_bfms
+import pybfms
+from wishbone_bfms.wb_initiator_bfm import WbInitiatorBfm
+from logic_analyzer_bfms.la_initiator_bfm import LaInitiatorBfm
 
 
 @cocotb.test()
 async def test(root):
-    print("Hello")
+    await pybfms.init()
+    u_wb : WbInitiatorBfm = pybfms.find_bfm(".*u_wb")
+    u_la : LaInitiatorBfm = pybfms.find_bfm(".*u_la")
+   
+    print("--> u_la.set_bits")
+    await u_la.set_bits(0, 0x55AAEEFF, 0xFFFFFFFF)
+    print("<-- u_la.set_bits")
+#    print("--> u_la.propagate")
+#    await u_la.propagate()
+#    print("<-- u_la.propagate")
+    
+    for i in range(10):
+        await u_wb.write(0x0, 0x55aa+i, 0xF)
+        dat = await u_wb.read(0x0)
+        print("dat=" + hex(dat))
+        
 
diff --git a/dv/bringup/python/bringup_tests/__init__.py b/dv/bringup/python/bringup_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/dv/bringup/python/bringup_tests/__init__.py
diff --git a/dv/bringup/python/bringup_tests/__pycache__/__init__.cpython-36.pyc b/dv/bringup/python/bringup_tests/__pycache__/__init__.cpython-36.pyc
new file mode 100644
index 0000000..8a49a25
--- /dev/null
+++ b/dv/bringup/python/bringup_tests/__pycache__/__init__.cpython-36.pyc
Binary files differ
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
new file mode 100644
index 0000000..3ed6cd8
--- /dev/null
+++ b/dv/bringup/python/bringup_tests/__pycache__/mgmt_mem_access.cpython-36.pyc
Binary files differ
diff --git a/dv/bringup/python/bringup_tests/la_reset_dut.py b/dv/bringup/python/bringup_tests/la_reset_dut.py
new file mode 100644
index 0000000..e8c293f
--- /dev/null
+++ b/dv/bringup/python/bringup_tests/la_reset_dut.py
@@ -0,0 +1,28 @@
+'''
+Created on Nov 22, 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 bringup_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")
+    
+    la_utils = LaUtils(u_la) 
+    
+    await la_utils.set_dut_clock_control(True)
+    await la_utils.reset_cycle_dut(100)
+
+    
\ No newline at end of file
diff --git a/dv/bringup/python/bringup_tests/la_utils.py b/dv/bringup/python/bringup_tests/la_utils.py
new file mode 100644
index 0000000..90976e8
--- /dev/null
+++ b/dv/bringup/python/bringup_tests/la_utils.py
@@ -0,0 +1,50 @@
+'''
+Created on Nov 22, 2020
+
+@author: mballance
+'''
+
+class LaUtils(object):
+    CLOCK_IDX = 127
+    RESET_IDX = 126
+    
+    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, 1, 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 reset_cycle_dut(self, cycles=10):
+        # Set reset high
+        await self.la_bfm.set_bits(LaUtils.RESET_IDX, 1, 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, 0, 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/bringup/python/bringup_tests/mgmt_mem_access.py b/dv/bringup/python/bringup_tests/mgmt_mem_access.py
new file mode 100644
index 0000000..fbf7a38
--- /dev/null
+++ b/dv/bringup/python/bringup_tests/mgmt_mem_access.py
@@ -0,0 +1,39 @@
+'''
+Created on Nov 22, 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
+
+
+@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")
+    
+    # Test that we can write and read dut 'ROM'
+    wr_data = []
+    r = Random(0)
+    for i in range(16):
+        data = r.randint(0, 0xFFFFFFFF)
+        print("Write: " + hex(0x80000000+4*i) + " = " + hex(data))
+        await u_wb.write(0x80000000 + 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(0x80000000 + 4*i)
+        if wr_data[i] == data:
+            print("PASS: " + hex(0x80000000+4*i))
+        else:
+            print("FAIL: " + hex(0x80000000+4*i) + " expect " + hex(wr_data[i]) + " receive " + hex(data))
+            
diff --git a/dv/common/icarus.mk b/dv/common/icarus.mk
new file mode 100644
index 0000000..f0fc7c5
--- /dev/null
+++ b/dv/common/icarus.mk
@@ -0,0 +1,54 @@
+#****************************************************************************
+#* icarus.mk
+#*
+#* Simulator support for Icarus Verilog
+#*
+#* SRCS           - List of source files
+#* INCDIRS        - Include paths
+#* DEFINES        - Defines
+#* PYBFMS_MODULES - Modules to query for BFMs
+#* SIM_ARGS       - generic simulation arguments
+#* VPI_LIBS       - List of PLI libraries
+#* DPI_LIBS       - List of DPI libraries
+#* TIMEOUT        - Simulation timeout, in units of ns,us,ms,s
+#****************************************************************************
+
+COMMON_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+PACKAGES_DIR := $(abspath $(COMMON_DIR)/../../packages)
+VLSIM := $(PACKAGES_DIR)/python/bin/vlsim
+PYBFMS_VPI_LIB := $(shell $(PACKAGES_DIR)/python/bin/pybfms lib)
+
+SIMV=simv.vvp
+ifneq (,$(DEBUG))
+VLSIM_OPTIONS += --trace-fst
+SIMV_ARGS += +vlsim.trace
+SIMV := simv.debug
+else
+SIMV := simv.ndebug
+endif
+
+# Enable VPI for Verilator
+VLSIM_OPTIONS += --vpi
+VLSIM_OPTIONS += --top-module $(TOP_MODULE)
+
+IVERILOG_OPTIONS += $(foreach inc,$(INCDIRS),-I $(inc))
+IVERILOG_OPTIONS += $(foreach def,$(DEFINES),-D $(def))
+VVP_ARGS += $(foreach vpi,$(VPI_LIBS),-m $(vpi))
+
+VPI_LIBS += $(PYBFMS_DPI_LIB)
+
+build : $(SIMV)
+
+$(SIMV) : $(SRCS) pybfms_gen.v
+	iverilog -o $@ $(IVERILOG_OPTIONS) $(SRCS) pybfms_gen.v 
+
+run : $(SIMV)
+	vvp $(SIMV) $(VVP_ARGS)
+	
+pybfms_gen.v :
+	$(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
diff --git a/dv/common/questa.mk b/dv/common/questa.mk
new file mode 100644
index 0000000..e68ccba
--- /dev/null
+++ b/dv/common/questa.mk
@@ -0,0 +1,45 @@
+#****************************************************************************
+#* questa.mk
+#*
+#* Simulator support for Mentor Questa
+#*
+#* SRCS           - List of source files
+#* INCDIRS        - Include paths
+#* DEFINES        - Defines
+#* TOP_MODULE     - Top module to load
+#* SIM_ARGS       - generic simulation arguments
+#* QUESTA_SIM_ARGS - vlsim-specific simulation arguments
+#* VPI_LIBS       - List of PLI libraries
+#* DPI_LIBS       - List of DPI libraries
+#* TIMEOUT        - Simulation timeout, in units of ns,us,ms,s
+#****************************************************************************
+
+COMMON_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
+PACKAGES_DIR := $(abspath $(COMMON_DIR)/../../packages)
+
+ifneq (,$(DEBUG))
+VLSIM_OPTIONS += --trace-fst
+SIMV_ARGS += +vlsim.trace
+SIMV := simv.debug
+else
+SIMV := simv.ndebug
+endif
+
+# Enable VPI for Verilator
+VLSIM_OPTIONS += --vpi --public-flat-rw
+
+VLSIM_OPTIONS += $(foreach inc,$(INCDIRS),+incdir+$(inc))
+VLSIM_OPTIONS += $(foreach def,$(DEFINES),+define+$(def))
+SIMV_ARGS += $(foreach vpi,$(VPI_LIBS),+vpi=$(vpi))
+
+build : $(SIMV)
+
+$(SIMV) : $(SRCS)
+	$(VLSIM) -o $@ $(VLSIM_CLKSPEC) $(VLSIM_OPTIONS) $(SRCS)
+
+run : $(SIMV)
+	./$(SIMV) $(SIMV_ARGS)
+
+clean ::
+	rm -f simv.* simx.fst simx.vcd
+	rm -rf obj_dir
diff --git a/dv/common/vlsim.mk b/dv/common/vlsim.mk
index 1a34b7e..184767b 100644
--- a/dv/common/vlsim.mk
+++ b/dv/common/vlsim.mk
@@ -6,6 +6,7 @@
 #* SRCS           - List of source files
 #* INCDIRS        - Include paths
 #* DEFINES        - Defines
+#* PYBFMS_MODULES - Modules to query for BFMs
 #* SIM_ARGS       - generic simulation arguments
 #* VLSIM_SIM_ARGS - vlsim-specific simulation arguments
 #* VLSIM_CLKSPEC  - clock-generation options for VLSIM
@@ -17,6 +18,7 @@
 COMMON_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
 PACKAGES_DIR := $(abspath $(COMMON_DIR)/../../packages)
 VLSIM := $(PACKAGES_DIR)/python/bin/vlsim
+PYBFMS_DPI_LIB := $(shell $(PACKAGES_DIR)/python/bin/pybfms lib)
 
 ifneq (,$(DEBUG))
 VLSIM_OPTIONS += --trace-fst
@@ -26,20 +28,29 @@
 SIMV := simv.ndebug
 endif
 
-VLSIM_OPTIONS += --vpi --public-flat-rw
+# Enable VPI for Verilator
+VLSIM_OPTIONS += --vpi
+VLSIM_OPTIONS += --top-module $(TOP_MODULE)
 
 VLSIM_OPTIONS += $(foreach inc,$(INCDIRS),+incdir+$(inc))
 VLSIM_OPTIONS += $(foreach def,$(DEFINES),+define+$(def))
 SIMV_ARGS += $(foreach vpi,$(VPI_LIBS),+vpi=$(vpi))
 
+DPI_LIBS += $(PYBFMS_DPI_LIB)
+
 build : $(SIMV)
 
-$(SIMV) : $(SRCS)
-	$(VLSIM) -o $@ $(VLSIM_CLKSPEC) $(VLSIM_OPTIONS) $(SRCS)
+$(SIMV) : $(SRCS) pybfms_gen.sv pybfms_gen.c
+	$(VLSIM) -o $@ $(VLSIM_CLKSPEC) $(VLSIM_OPTIONS) $(SRCS) pybfms_gen.sv pybfms_gen.c \
+		$(foreach l,$(DPI_LIBS),$(l))
 
 run : $(SIMV)
 	./$(SIMV) $(SIMV_ARGS)
+	
+pybfms_gen.sv :
+	$(PACKAGES_DIR)/python/bin/pybfms generate \
+		-l sv $(foreach m,$(PYBFMS_MODULES),-m $(m)) -o $@
 
 clean ::
-	rm -f simv.* simx.fst simx.vcd
+	rm -f simv.* simx.fst simx.vcd pybfms_gen.sv pybfms_gen.c
 	rm -rf obj_dir
diff --git a/etc/packages.mf b/etc/packages.mf
index 254989b..482dac7 100644
--- a/etc/packages.mf
+++ b/etc/packages.mf
@@ -1,3 +1,4 @@
 
 fwrisc@https://github.com/mballance/fwrisc.git
+wb_sys_ip@https://github.com/mballance/wb_sys_ip.git
 
diff --git a/requirements.txt b/requirements.txt
index d071710..11f4039 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,4 +4,5 @@
 
 -e git+https://github.com/pybfms/pybfms.git#egg=pybfms
 -e git+https://github.com/pybfms/wishbone_bfms.git#egg=wishbone_bfms
+-e git+https://github.com/pybfms/pybfms_logic_analyzer.git#egg=pybfms-logic-analyzer
 
diff --git a/rtl/spram.v b/rtl/spram.v
deleted file mode 100644
index 3eb911e..0000000
--- a/rtl/spram.v
+++ /dev/null
@@ -1,35 +0,0 @@
-
-module spram_byte_en #(
-	parameter ADDR_BITS = 9, // 8=1KB 9=2KB 10=4KB
-	parameter DATA_BITS = 32
-	) (
-	input				clock,
-	// Port A
-	input  [ADDR_BITS-1:0]		a_adr,
-	input  [DATA_BITS-1:0]		a_dat_i,
-	output reg [DATA_BITS-1:0]	a_dat_o,
-	input				a_we,
-	input  [DATA_BITS/8-1:0]	a_sel
-
-	);
-
-	reg[7:0]		ram_0[(1 << ADDR_BITS)-1:0];
-	reg[7:0]		ram_1[(1 << ADDR_BITS)-1:0];
-	reg[7:0]		ram_2[(1 << ADDR_BITS)-1:0];
-	reg[7:0]		ram_3[(1 << ADDR_BITS)-1:0];
-
-	always @(posedge clock) begin
-		if (a_we) begin
-			if (a_sel[0]) ram_0[a_adr] <= a_dat_i[7:0];
-			if (a_sel[1]) ram_1[a_adr] <= a_dat_i[15:8];
-			if (a_sel[2]) ram_2[a_adr] <= a_dat_i[23:16];
-			if (a_sel[3]) ram_3[a_adr] <= a_dat_i[31:24];
-		end
-		a_dat_o[7:0]   <= ram_0[a_adr];
-		a_dat_o[15:8]  <= ram_1[a_adr];
-		a_dat_o[23:16] <= ram_2[a_adr];
-		a_dat_o[31:24] <= ram_2[a_adr];
-	end
-
-endmodule
-
diff --git a/rtl/fwpayload.v b/verilog/rtl/fwpayload.v
similarity index 65%
rename from rtl/fwpayload.v
rename to verilog/rtl/fwpayload.v
index 9ad390f..f585e22 100644
--- a/rtl/fwpayload.v
+++ b/verilog/rtl/fwpayload.v
@@ -48,12 +48,95 @@
 		);
 	
 	wire clk, rst;
+
+	// System interconnect
+	localparam N_INITIATORS = 3;
+	localparam INIT_ID_CORE_I = 0;
+	localparam INIT_ID_CORE_D = 1;
+	localparam INIT_ID_MGMT   = 2;
+	
+	localparam N_TARGETS = 1;
+	localparam TGT_ID_SRAM = 0;
+	wire[31:0]		IC_I_ADR[N_INITIATORS-1:0];
+	wire[31:0]		IC_I_DAT_W[N_INITIATORS-1:0];
+	wire[31:0]		IC_I_DAT_R[N_INITIATORS-1:0];
+	wire			IC_I_CYC[N_INITIATORS-1:0];
+	wire			IC_I_ERR[N_INITIATORS-1:0];
+	wire[3:0]		IC_I_SEL[N_INITIATORS-1:0];
+	wire			IC_I_STB[N_INITIATORS-1:0];
+	wire			IC_I_ACK[N_INITIATORS-1:0];
+	wire			IC_I_WE[N_INITIATORS-1:0];
+	
+	wire[31:0]		IC_T_ADR[N_TARGETS:0];
+	wire[31:0]		IC_T_DAT_W[N_TARGETS:0];
+	wire[31:0]		IC_T_DAT_R[N_TARGETS:0];
+	wire			IC_T_CYC[N_TARGETS:0];
+	wire			IC_T_ERR[N_TARGETS:0];
+	wire[3:0]		IC_T_SEL[N_TARGETS:0];
+	wire			IC_T_STB[N_TARGETS:0];
+	wire			IC_T_ACK[N_TARGETS:0];
+	wire			IC_T_WE[N_TARGETS:0];
+	
+	assign IC_T_ACK[N_TARGETS] = 1;
+	assign IC_T_ERR[N_TARGETS] = 1;
+	assign IC_T_DAT_R[N_TARGETS] = 0;
+	
+	// Interconnect
+	wb_interconnect_NxN #(
+			.WB_ADDR_WIDTH(32),
+			.WB_DATA_WIDTH(32),
+			.N_INITIATORS(N_INITIATORS),
+			.N_TARGETS(N_TARGETS),
+			.I_ADR_MASK({
+				{ 8'hFF, {24{1'b0}} }
+				}),
+			.T_ADR({
+				{ 32'h8000_0000 }
+				})
+		) 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)
+		);
+
+	/****************************************************************
+	 * Connect management interface to port 1 on the interconnect
+	 ****************************************************************/
+	assign IC_I_ADR[INIT_ID_MGMT] = wbs_adr_i;
+	assign IC_I_DAT_W[INIT_ID_MGMT] = wbs_dat_i;
+	assign wbs_dat_o = IC_I_DAT_R[INIT_ID_MGMT];
+	assign IC_I_CYC[INIT_ID_MGMT] = wbs_cyc_i;
+//	assign IC_I_ERR[INIT_ID_MGMT] = //wbs_cyc_i;
+	assign IC_I_SEL[INIT_ID_MGMT] = 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;
 	
 	// 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 clk = (~la_oen[127]) ? la_data_in[127]: wb_clk_i;
+//	assign rst = (~la_oen[126]) ? la_data_in[126]: wb_rst_i;
+	assign clk = wb_clk_i;
+	assign rst = wb_rst_i;
 	
 	wire[31:0]			iaddr;
 	reg[31:0]			idata;
@@ -70,8 +153,6 @@
 	localparam RAM_BITS = 8;
 	localparam ROM_BITS = 8;
 
-`ifdef UNDEFINED
-`endif
 	fwrisc_rv32i u_core (
 				.clock(clk),
 				.reset(rst),
@@ -89,6 +170,7 @@
 				.drdata(drdata),
 				.dready(dready)
 			);
+
 	
 	// Probes
 	// - PC 
@@ -135,7 +217,7 @@
 	reg[7:0]			ram_2[(1 << RAM_BITS)-1:0]; // 16k ram
 	reg[7:0]			ram_3[(1 << RAM_BITS)-1:0]; // 16k ram
 	reg[31:0]			rom[(1 << ROM_BITS)-1:0];   // 16k rom
-	reg[31:0]			led;
+//	reg[31:0]			led;
 	reg[31:0]			tx_r;
 	reg					iready_r, dready_r;
 	
@@ -189,28 +271,54 @@
 	end
 
 	/****************************************************************
-	 * Simple WB to storage bridge
+	 * Simple WB to SRAM bridge
 	 ****************************************************************/
 	reg[1:0] wb_bridge_state = 0;
+	wire[31:0] sram_adr_i = IC_T_ADR[TGT_ID_SRAM];
+	wire[31:0] sram_dat_w = IC_T_DAT_W[TGT_ID_SRAM];
+	wire[31:0] sram_dat_r;
+	assign IC_T_DAT_R[TGT_ID_SRAM] = 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[TGT_ID_SRAM];
+	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];
 
 	always @(posedge wb_clk_i) begin
-		if (wb_rst_i == 1) begin
+		if (rst == 1) begin
 			wb_bridge_state <= 0;
 		end else begin
 			case (wb_bridge_state)
 				0:
-					if (wbs_cyc_i && wbs_stb_i) begin
+					if (sram_cyc_i && sram_stb_i) begin
 						wb_bridge_state <= 1;
 					end
 				1:
 					wb_bridge_state <= 2;
 				2:
+					wb_bridge_state <= 3;
+				3:
 					wb_bridge_state <= 0;
 				default:
 					wb_bridge_state <= 0;
 			endcase
 		end
 	end
+	
+	/****************************************************************
+	 * SRAM
+	 ****************************************************************/
+	spram_32x256 u_sram(
+			.clock(clk),
+			.a_adr(sram_adr_i),
+			.a_dat_i(sram_dat_w),
+			.a_dat_o(sram_dat_r),
+			.a_we(sram_we_i),
+			.a_sel(sram_sel_i));
+	assign sram_ack_o = (wb_bridge_state == 3);
+	
 
 	wire [31:0] storage_mgmt_addr    = wbs_adr_i; // [ADDRESS_WIDTH+(DATA_WIDTH/32):(DATA_WIDTH/32)+1];
 	wire storage_mgmt_rd_en          = (wbs_cyc_i & wbs_stb_i & !wbs_we_i);
diff --git a/verilog/rtl/simple_spi_master.v b/verilog/rtl/simple_spi_master.v
new file mode 100755
index 0000000..4cf8b46
--- /dev/null
+++ b/verilog/rtl/simple_spi_master.v
@@ -0,0 +1,390 @@
+//----------------------------------------------------------------------------
+// Module: simple_spi_master
+//
+//----------------------------------------------------------------------------
+// Copyright (C) 2019 efabless, inc.
+//
+// This source file may be used and distributed without
+// restriction provided that this copyright statement is not
+// removed from the file and that any derivative work contains
+// the original copyright notice and the associated disclaimer.
+//
+// This source file is free software; you can redistribute it
+// and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation;
+// either version 2.1 of the License, or (at your option) any
+// later version.
+//
+// This source is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied
+// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+// PURPOSE.  See the GNU Lesser General Public License for more
+// details.
+//
+//--------------------------------------------------------------------
+// 
+// resetn: active low async reset
+// clk:    master clock (before prescaler)
+// stream:
+//     0 = apply/release CSB separately for each byte
+//     1 = apply CSB until stream bit is cleared
+// mlb:
+//     0 = msb 1st
+//     1 = lsb 1st
+// invsck:
+//     0 = normal SCK
+//     1 = inverted SCK
+// invcsb:
+//     0 = normal CSB (active low)
+//     1 = inverted CSB (active high)
+// mode:
+//     0 = read and change data on opposite SCK edges
+//     1 = read and change data on the same SCK edge
+// enable:
+//     0 = disable the SPI master
+//     1 = enable the SPI master
+// irqena:
+//     0 = disable interrupt
+//     1 = enable interrupt
+// hkconn:
+//     0 = housekeeping SPI disconnected
+//     1 = housekeeping SPI connected (when SPI master enabled)
+// prescaler: count (in master clock cycles) of 1/2 SCK cycle.
+//
+// reg_dat_we:
+//     1 = data write enable
+// reg_dat_re:
+//     1 = data read enable
+// reg_cfg_*: Signaling for read/write of configuration register
+// reg_dat_*: Signaling for read/write of data register
+//
+// err_out:  Indicates attempt to read/write before data ready
+//	(failure to wait for reg_dat_wait to clear)
+//
+// Between "mode" and "invsck", all four standard SPI modes are supported
+//
+//--------------------------------------------------------------------
+
+module simple_spi_master_wb #(
+    parameter BASE_ADR = 32'h2100_0000,
+    parameter CONFIG = 8'h00,
+    parameter DATA = 8'h04
+) (
+    input wb_clk_i,
+    input wb_rst_i,
+    input [31:0] wb_adr_i,
+    input [31:0] wb_dat_i,
+    input [3:0] wb_sel_i,
+    input wb_we_i,
+    input wb_cyc_i,
+    input wb_stb_i,
+    output wb_ack_o,
+    output [31:0] wb_dat_o,
+
+    output	 hk_connect,	// Connect to housekeeping SPI
+    input 	 sdi,	 // SPI input
+    output 	 csb,	 // SPI chip select
+    output 	 sck,	 // SPI clock
+    output 	 sdo,	 // SPI output
+    output 	 sdoenb, // SPI output enable
+    output	 irq	 // interrupt output
+);
+
+    wire [31:0] simple_spi_master_reg_cfg_do;
+    wire [31:0] simple_spi_master_reg_dat_do;
+
+    wire resetn = ~wb_rst_i;
+    wire valid = wb_stb_i && wb_cyc_i;
+    wire simple_spi_master_reg_cfg_sel = valid && (wb_adr_i == (BASE_ADR | CONFIG));
+    wire simple_spi_master_reg_dat_sel = valid && (wb_adr_i == (BASE_ADR | DATA));
+
+    wire [1:0] reg_cfg_we = (simple_spi_master_reg_cfg_sel) ?
+		(wb_sel_i[1:0] & {2{wb_we_i}}): 2'b00;
+    wire reg_dat_we = (simple_spi_master_reg_dat_sel) ? (wb_sel_i[0] & wb_we_i): 1'b0;
+    wire reg_dat_wait;
+
+    wire [31:0] mem_wdata = wb_dat_i;
+    wire reg_dat_re = simple_spi_master_reg_dat_sel && !wb_sel_i && ~wb_we_i;
+
+    assign wb_dat_o = (simple_spi_master_reg_cfg_sel) ? simple_spi_master_reg_cfg_do :
+		simple_spi_master_reg_dat_do;
+    assign wb_ack_o = (simple_spi_master_reg_cfg_sel || simple_spi_master_reg_dat_sel)
+		&& (!reg_dat_wait);
+
+    simple_spi_master spi_master (
+    	.resetn(resetn),
+    	.clk(wb_clk_i),
+    	.reg_cfg_we(reg_cfg_we),
+    	.reg_cfg_di(mem_wdata),
+    	.reg_cfg_do(simple_spi_master_reg_cfg_do),
+    	.reg_dat_we(reg_dat_we),
+    	.reg_dat_re(reg_dat_re),
+    	.reg_dat_di(mem_wdata),
+    	.reg_dat_do(simple_spi_master_reg_dat_do),
+    	.reg_dat_wait(reg_dat_wait),
+
+	.hk_connect(hk_connect),	// Attach to housekeeping SPI slave
+    	.sdi(sdi),	 // SPI input
+    	.csb(csb),	 // SPI chip select
+    	.sck(sck),	 // SPI clock
+    	.sdo(sdo),	 // SPI output
+	.irq_out(irq)	 // interrupt
+    );
+endmodule
+
+module simple_spi_master (
+    input        resetn,
+    input        clk,	 // master clock (assume 100MHz)
+
+    input  [1:0]  reg_cfg_we,
+    input  [31:0] reg_cfg_di,
+    output [31:0] reg_cfg_do,
+
+    input  	  reg_dat_we,
+    input  	  reg_dat_re,
+    input  [31:0] reg_dat_di,
+    output [31:0] reg_dat_do,
+    output	  reg_dat_wait,
+    output	  irq_out,
+    output	  err_out,
+
+    output	 hk_connect,	// Connect to housekeeping SPI
+    input 	 sdi,	 // SPI input
+    output 	 csb,	 // SPI chip select
+    output 	 sck,	 // SPI clock
+    output 	 sdo	 // SPI output
+);
+
+    parameter IDLE   = 2'b00;	    
+    parameter SENDL  = 2'b01; 
+    parameter SENDH  = 2'b10; 
+    parameter FINISH = 2'b11; 
+
+    reg	  done;
+    reg 	  isdo, hsck, icsb;
+    reg [1:0] state;
+    reg 	  isck;
+    reg	  err_out;
+ 
+    reg [7:0]  treg, rreg, d_latched;
+    reg [2:0]  nbit;
+
+    reg [7:0]  prescaler;
+    reg [7:0]  count;
+    reg	   invsck;
+    reg	   invcsb;
+    reg	   mlb;
+    reg	   irqena;
+    reg	   stream;
+    reg	   mode;
+    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;
+
+    // No bidirectional 3-pin mode defined, so SDO is enabled whenever CSB is low.
+    assign	  sdoenb = icsb;
+    // assign	  sdo = (enable == 1'b0) ? 1'bz : icsb ? 1'bz : isdo;
+    assign	  sdo = (enable == 1'b0) ? 1'bz : isdo;
+
+    assign	  irq_out = irqena & done;
+    assign	  hk_connect = (enable == 1'b1) ? hkconn : 1'b0;
+
+    // Read configuration and data registers
+    assign reg_cfg_do = {16'd0, hkconn, irqena, enable, stream, mode,
+			 invsck, invcsb, mlb, prescaler};
+    assign reg_dat_wait = ~done;
+    assign reg_dat_do = done ? rreg : ~0;
+
+    // Write configuration register
+    always @(posedge clk or negedge resetn) begin
+        if (resetn == 1'b0) begin
+	    prescaler <= 8'd2;
+	    invcsb <= 1'b0;
+	    invsck <= 1'b0;
+	    mlb <= 1'b0;
+	    enable <= 1'b0;
+	    irqena <= 1'b0;
+	    stream <= 1'b0;
+	    mode <= 1'b0;
+	    hkconn <= 1'b0;
+        end else begin
+            if (reg_cfg_we[0]) prescaler <= reg_cfg_di[7:0];
+            if (reg_cfg_we[1]) begin
+	        mlb <= reg_cfg_di[8];
+	        invcsb <= reg_cfg_di[9];
+	        invsck <= reg_cfg_di[10];
+	        mode <= reg_cfg_di[11];
+	        stream <= reg_cfg_di[12];
+	        enable <= reg_cfg_di[13];
+	        irqena <= reg_cfg_di[14];
+	        hkconn <= reg_cfg_di[15];
+	    end //reg_cfg_we[1]
+        end //resetn
+    end //always
+ 
+    // Watch for read and write enables on clk, not hsck, so as not to
+    // miss them.
+
+    reg w_latched, r_latched;
+
+    always @(posedge clk or negedge resetn) begin
+        if (resetn == 1'b0) begin
+	    err_out <= 1'b0;
+            w_latched <= 1'b0;
+            r_latched <= 1'b0;
+	    d_latched <= 8'd0;
+        end else begin
+            // Clear latches on SEND, otherwise latch when seen
+            if (state == SENDL || state == SENDH) begin
+	        if (reg_dat_we == 1'b0) begin
+		    w_latched <= 1'b0;
+	        end
+	    end else begin
+	        if (reg_dat_we == 1'b1) begin
+		    if (done == 1'b0 && w_latched == 1'b1) begin
+		        err_out <= 1'b1;
+		    end else begin
+		        w_latched <= 1'b1;
+		        d_latched <= reg_dat_di[7:0];
+		        err_out <= 1'b0;
+		    end
+	        end
+	    end
+
+	    if (reg_dat_re == 1'b1) begin
+	        if (r_latched == 1'b1) begin
+		    r_latched <= 1'b0;
+	        end else begin
+		    err_out <= 1'b1;	// byte not available
+	        end
+	    end else if (state == FINISH) begin
+	        r_latched <= 1'b1;
+	    end if (state == SENDL || state == SENDH) begin
+	        if (r_latched == 1'b1) begin
+		    err_out <= 1'b1;	// last byte was never read
+	        end else begin
+		    r_latched <= 1'b0;
+	        end
+	    end
+        end
+    end
+
+    // State transition.
+
+    always @(posedge hsck or negedge resetn) begin
+        if (resetn == 1'b0) begin
+	    state <= IDLE;
+	    nbit <= 3'd0;
+	    icsb <= 1'b1;
+	    done <= 1'b1;
+        end else begin
+	    if (state == IDLE) begin
+	        if (w_latched == 1'b1) begin
+		    state <= SENDL;
+		    nbit <= 3'd0;
+		    icsb <= 1'b0;
+		    done <= 1'b0;
+	        end else begin
+	            icsb <= ~stream;
+	        end
+	    end else if (state == SENDL) begin
+	        state <= SENDH;
+	    end else if (state == SENDH) begin
+	        nbit <= nbit + 1;
+                if (nbit == 3'd7) begin
+		    state <= FINISH;
+	        end else begin
+	            state <= SENDL;
+	        end
+	    end else if (state == FINISH) begin
+	        icsb <= ~stream;
+	        done <= 1'b1;
+	        state <= IDLE;
+	    end
+        end
+    end
+ 
+    // Set up internal clock.  The enable bit gates the internal clock
+    // to shut down the master SPI when disabled.
+
+    always @(posedge clk or negedge resetn) begin
+        if (resetn == 1'b0) begin
+	    count <= 8'd0;
+	    hsck <= 1'b0;
+        end else begin
+	    if (enable == 1'b0) begin
+ 	        count <= 8'd0;
+	    end else begin
+	        count <= count + 1; 
+                if (count == prescaler) begin
+		    hsck <= ~hsck;
+		    count <= 8'd0;
+	        end // count
+	    end // enable
+        end // resetn
+    end // always
+ 
+    // sck is half the rate of hsck
+
+    always @(posedge hsck or negedge resetn) begin
+        if (resetn == 1'b0) begin
+	    isck <= 1'b0;
+        end else begin
+	    if (state == IDLE || state == FINISH)
+	        isck <= 1'b0;
+	    else
+	        isck <= ~isck;
+        end // resetn
+    end // always
+
+    // Main procedure:  read, write, shift data
+
+    always @(posedge hsck or negedge resetn) begin
+        if (resetn == 1'b0) begin
+	    rreg <= 8'hff;
+	    treg <= 8'hff;
+	    isdo <= 1'b0;
+        end else begin 
+	    if (isck == 1'b0 && (state == SENDL || state == SENDH)) begin
+	        if (mlb == 1'b1) begin
+		    // LSB first, sdi@msb -> right shift
+		    rreg <= {sdi, rreg[7:1]};
+	        end else begin
+		    // MSB first, sdi@lsb -> left shift
+		    rreg <= {rreg[6:0], sdi};
+	        end
+	    end // read on ~isck
+
+            if (w_latched == 1'b1) begin
+	        if (mlb == 1'b1) begin
+		    treg <= {1'b1, d_latched[7:1]};
+		    isdo <= d_latched[0];
+	        end else begin
+		    treg <= {d_latched[6:0], 1'b1};
+		    isdo <= d_latched[7];
+	        end // mlb
+	    end else if ((mode ^ isck) == 1'b1) begin
+	        if (mlb == 1'b1) begin
+		    // LSB first, shift right
+		    treg <= {1'b1, treg[7:1]};
+		    isdo <= treg[0];
+	        end else begin
+		    // MSB first shift LEFT
+		    treg <= {treg[6:0], 1'b1};
+		    isdo <= treg[7];
+	        end // mlb
+	    end // write on mode ^ isck
+        end // resetn
+    end // always
+ 
+endmodule
diff --git a/verilog/rtl/simpleuart.v b/verilog/rtl/simpleuart.v
new file mode 100644
index 0000000..96b3b4e
--- /dev/null
+++ b/verilog/rtl/simpleuart.v
@@ -0,0 +1,220 @@
+/*
+ *  PicoSoC - A simple example SoC using PicoRV32
+ *
+ *  Copyright (C) 2017  Clifford Wolf <clifford@clifford.at>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+module simpleuart_wb # (
+    parameter BASE_ADR = 32'h 2000_0000,
+    parameter CLK_DIV = 8'h00,
+    parameter DATA = 8'h04,
+    parameter CONFIG = 8'h08
+) (
+    input wb_clk_i,
+    input wb_rst_i,
+
+    input [31:0] wb_adr_i,      // (verify): input address was originaly 22 bits , why ? (max number of words ?)
+    input [31:0] wb_dat_i,
+    input [3:0]  wb_sel_i,
+    input wb_we_i,
+    input wb_cyc_i,
+    input wb_stb_i,
+
+    output wb_ack_o,
+    output [31:0] wb_dat_o,
+
+    output uart_enabled,
+    output ser_tx,
+    input  ser_rx
+
+);
+    wire [31:0] simpleuart_reg_div_do;
+    wire [31:0] simpleuart_reg_dat_do;
+    wire [31:0] simpleuart_reg_cfg_do;
+
+    wire resetn = ~wb_rst_i;
+    wire valid = wb_stb_i && wb_cyc_i; 
+    wire simpleuart_reg_div_sel = valid && (wb_adr_i == (BASE_ADR | CLK_DIV));
+    wire simpleuart_reg_dat_sel = valid && (wb_adr_i == (BASE_ADR | DATA));
+    wire simpleuart_reg_cfg_sel = valid && (wb_adr_i == (BASE_ADR | CONFIG));
+
+    wire [3:0] reg_div_we = simpleuart_reg_div_sel ? (wb_sel_i & {4{wb_we_i}}): 4'b 0000; 
+    wire reg_dat_we = simpleuart_reg_dat_sel ? (wb_sel_i[0] & wb_we_i): 1'b 0;      // simpleuart_reg_dat_sel ? mem_wstrb[0] : 1'b 0
+    wire reg_cfg_we = simpleuart_reg_cfg_sel ? (wb_sel_i[0] & wb_we_i): 1'b 0; 
+
+    wire [31:0] mem_wdata = wb_dat_i;
+    wire reg_dat_re = simpleuart_reg_dat_sel && !wb_sel_i && ~wb_we_i; // read_enable
+
+    wire reg_dat_wait;
+
+    assign wb_dat_o = simpleuart_reg_div_sel ? simpleuart_reg_div_do:
+		      simpleuart_reg_cfg_sel ? simpleuart_reg_cfg_do:
+					       simpleuart_reg_dat_do;
+    assign wb_ack_o = (simpleuart_reg_div_sel || simpleuart_reg_dat_sel
+			|| simpleuart_reg_cfg_sel) && (!reg_dat_wait);
+    
+    simpleuart simpleuart (
+        .clk    (wb_clk_i),
+        .resetn (resetn),
+
+        .ser_tx      (ser_tx),
+        .ser_rx      (ser_rx),
+	.enabled     (uart_enabled),
+
+        .reg_div_we  (reg_div_we), 
+        .reg_div_di  (mem_wdata),
+        .reg_div_do  (simpleuart_reg_div_do),
+
+        .reg_cfg_we  (reg_cfg_we), 
+        .reg_cfg_di  (mem_wdata),
+        .reg_cfg_do  (simpleuart_reg_cfg_do),
+
+        .reg_dat_we  (reg_dat_we),
+        .reg_dat_re  (reg_dat_re),
+        .reg_dat_di  (mem_wdata),
+        .reg_dat_do  (simpleuart_reg_dat_do),
+        .reg_dat_wait(reg_dat_wait)
+    );
+
+endmodule
+
+module simpleuart (
+    input clk,
+    input resetn,
+
+    output enabled,
+    output ser_tx,
+    input  ser_rx,
+
+    input   [3:0] reg_div_we,         
+    input  [31:0] reg_div_di,         
+    output [31:0] reg_div_do,         
+
+    input   	  reg_cfg_we,         
+    input  [31:0] reg_cfg_di,         
+    output [31:0] reg_cfg_do,         
+
+    input         reg_dat_we,         
+    input         reg_dat_re,         
+    input  [31:0] reg_dat_di,
+    output [31:0] reg_dat_do,
+    output        reg_dat_wait
+);
+    reg [31:0] cfg_divider;
+    reg        enabled;
+
+    reg [3:0] recv_state;
+    reg [31:0] recv_divcnt;
+    reg [7:0] recv_pattern;
+    reg [7:0] recv_buf_data;
+    reg recv_buf_valid;
+
+    reg [9:0] send_pattern;
+    reg [3:0] send_bitcnt;
+    reg [31:0] send_divcnt;
+    reg send_dummy;
+
+    assign reg_div_do = cfg_divider;
+    assign reg_ena_do = {31'd0, enabled};
+
+    assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy);
+    assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0;
+
+    always @(posedge clk) begin
+        if (!resetn) begin
+            cfg_divider <= 1;
+	    enabled <= 1'b0;
+        end else begin
+            if (reg_div_we[0]) cfg_divider[ 7: 0] <= reg_div_di[ 7: 0];
+            if (reg_div_we[1]) cfg_divider[15: 8] <= reg_div_di[15: 8];
+            if (reg_div_we[2]) cfg_divider[23:16] <= reg_div_di[23:16];
+            if (reg_div_we[3]) cfg_divider[31:24] <= reg_div_di[31:24];
+            if (reg_cfg_we) enabled <= reg_div_di[0];
+        end
+    end
+
+    always @(posedge clk) begin
+        if (!resetn) begin
+            recv_state <= 0;
+            recv_divcnt <= 0;
+            recv_pattern <= 0;
+            recv_buf_data <= 0;
+            recv_buf_valid <= 0;
+        end else begin
+            recv_divcnt <= recv_divcnt + 1;
+            if (reg_dat_re)
+                recv_buf_valid <= 0;
+            case (recv_state)
+                0: begin
+                    if (!ser_rx && enabled)
+                        recv_state <= 1;
+                    recv_divcnt <= 0;
+                end
+                1: begin
+                    if (2*recv_divcnt > cfg_divider) begin
+                        recv_state <= 2;
+                        recv_divcnt <= 0;
+                    end
+                end
+                10: begin
+                    if (recv_divcnt > cfg_divider) begin
+                        recv_buf_data <= recv_pattern;
+                        recv_buf_valid <= 1;
+                        recv_state <= 0;
+                    end
+                end
+                default: begin
+                    if (recv_divcnt > cfg_divider) begin
+                        recv_pattern <= {ser_rx, recv_pattern[7:1]};
+                        recv_state <= recv_state + 1;
+                        recv_divcnt <= 0;
+                    end
+                end
+            endcase
+        end
+    end
+
+    assign ser_tx = send_pattern[0];
+
+    always @(posedge clk) begin
+        if (reg_div_we && enabled)
+            send_dummy <= 1;
+        send_divcnt <= send_divcnt + 1;
+        if (!resetn) begin
+            send_pattern <= ~0;
+            send_bitcnt <= 0;
+            send_divcnt <= 0;
+            send_dummy <= 1;
+        end else begin
+            if (send_dummy && !send_bitcnt) begin
+                send_pattern <= ~0;
+                send_bitcnt <= 15;
+                send_divcnt <= 0;
+                send_dummy <= 0;
+            end else
+            if (reg_dat_we && !send_bitcnt) begin
+                send_pattern <= {1'b1, reg_dat_di[7:0], 1'b0};
+                send_bitcnt <= 10;
+                send_divcnt <= 0;
+            end else
+            if (send_divcnt > cfg_divider && send_bitcnt) begin
+                send_pattern <= {1'b1, send_pattern[9:1]};
+                send_bitcnt <= send_bitcnt - 1;
+                send_divcnt <= 0;
+            end
+        end
+    end
+endmodule
diff --git a/verilog/rtl/spram.v b/verilog/rtl/spram.v
new file mode 100644
index 0000000..7961565
--- /dev/null
+++ b/verilog/rtl/spram.v
@@ -0,0 +1,33 @@
+
+module spram_byte_en #(
+	parameter ADDR_BITS = 9, // 8=1KB 9=2KB 10=4KB
+	parameter DATA_BITS = 32
+	) (
+	input				clock,
+	// Port A
+	input  [ADDR_BITS-1:0]		a_adr,
+	input  [DATA_BITS-1:0]		a_dat_i,
+	output reg [DATA_BITS-1:0]	a_dat_o,
+	input						a_we,
+	input  [DATA_BITS/8-1:0]	a_sel);
+
+	reg[7:0]		ram_0[(1 << ADDR_BITS)-1:0];
+	reg[7:0]		ram_1[(1 << ADDR_BITS)-1:0];
+	reg[7:0]		ram_2[(1 << ADDR_BITS)-1:0];
+	reg[7:0]		ram_3[(1 << ADDR_BITS)-1:0];
+
+	always @(posedge clock) begin
+		if (a_we) begin
+			if (a_sel[0]) ram_0[a_adr[ADDR_BITS-1:2]] <= a_dat_i[7:0];
+			if (a_sel[1]) ram_1[a_adr[ADDR_BITS-1:2]] <= a_dat_i[15:8];
+			if (a_sel[2]) ram_2[a_adr[ADDR_BITS-1:2]] <= a_dat_i[23:16];
+			if (a_sel[3]) ram_3[a_adr[ADDR_BITS-1:2]] <= a_dat_i[31:24];
+		end
+		a_dat_o[7:0]   <= ram_0[a_adr[ADDR_BITS-1:2]];
+		a_dat_o[15:8]  <= ram_1[a_adr[ADDR_BITS-1:2]];
+		a_dat_o[23:16] <= ram_2[a_adr[ADDR_BITS-1:2]];
+		a_dat_o[31:24] <= ram_3[a_adr[ADDR_BITS-1:2]];
+	end
+
+endmodule
+
diff --git a/verilog/rtl/spram_32x256.sv b/verilog/rtl/spram_32x256.sv
new file mode 100644
index 0000000..cea0682
--- /dev/null
+++ b/verilog/rtl/spram_32x256.sv
@@ -0,0 +1,31 @@
+/****************************************************************************
+ * spram_32x256.sv
+ ****************************************************************************/
+
+/**
+ * Module: spram_32x256
+ * 
+ * TODO: Add module documentation
+ */
+module spram_32x256(
+		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(8),
+			.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/rtl/user_project_wrapper.v b/verilog/rtl/user_project_wrapper.v
similarity index 100%
rename from rtl/user_project_wrapper.v
rename to verilog/rtl/user_project_wrapper.v
diff --git a/verilog/rtl/wb_interconnect_NxN.sv b/verilog/rtl/wb_interconnect_NxN.sv
new file mode 100644
index 0000000..b87cf56
--- /dev/null
+++ b/verilog/rtl/wb_interconnect_NxN.sv
@@ -0,0 +1,321 @@
+/****************************************************************************
+ * ${NAME}.sv
+ ****************************************************************************/
+
+/**
+ * Module: wb_interconnect_NxN
+ * 
+ * TODO: Add module documentation
+ */
+module wb_interconnect_NxN #(
+		parameter int 									WB_ADDR_WIDTH=32,
+		parameter int unsigned							WB_DATA_WIDTH=32,
+		parameter int unsigned							N_INITIATORS=1,
+		parameter int unsigned							N_TARGETS=1,
+		parameter bit [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-1:0]		ADR[N_INITIATORS-1:0],
+		input[WB_DATA_WIDTH-1:0]		DAT_W[N_INITIATORS-1:0],
+		output[WB_DATA_WIDTH-1:0]		DAT_R[N_INITIATORS-1:0],
+		input							CYC[N_INITIATORS-1:0],
+		output							ERR[N_INITIATORS-1:0],
+		input[(WB_DATA_WIDTH/8)-1:0]	SEL[N_INITIATORS-1:0],
+		input							STB[N_INITIATORS-1:0],
+		output							ACK[N_INITIATORS-1:0],
+		input							WE[N_INITIATORS-1:0],
+
+		output[WB_ADDR_WIDTH-1:0]		TADR[N_TARGETS:0],
+		output[WB_DATA_WIDTH-1:0]		TDAT_W[N_TARGETS:0],
+		input[WB_DATA_WIDTH-1:0]		TDAT_R[N_TARGETS:0],
+		output							TCYC[N_TARGETS:0],
+		input							TERR[N_TARGETS:0],
+		output[(WB_DATA_WIDTH/8)-1:0]	TSEL[N_TARGETS:0],
+		output							TSTB[N_TARGETS:0],
+		input							TACK[N_TARGETS:0],
+		output							TWE[N_TARGETS:0]
+		);
+	
+	localparam int WB_DATA_MSB = (WB_DATA_WIDTH-1);
+	localparam int N_INIT_ID_BITS = (N_INITIATORS>1)?$clog2(N_INITIATORS):1;
+	localparam int N_TARG_ID_BITS = $clog2(N_TARGETS+1);
+	localparam bit[N_TARG_ID_BITS:0]		NO_TARGET  = {(N_TARG_ID_BITS+1){1'b1}};
+	localparam bit[N_INIT_ID_BITS:0]		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(
+		reg[N_INIT_ID_BITS-1:0]		initiator,
+		reg[WB_ADDR_WIDTH-1:0] 		addr
+		);
+//		$display("addr2targ_id: 'h%08h 'h%08h", addr, ADDR_RANGES);
+		for (int 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);
+				return N_TARGETS-1;
+			end
+		end
+		$display("%t: Address 'h%08h - decode fail", $time, addr);
+		return (N_TARGETS);
+	endfunction
+	
+// Read request state machine
+
+	// Master 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++) 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[m_i]
+										);
+//								$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++) begin : block_m_req_i
+			for (m_req_j=0; m_req_j < N_INITIATORS; m_req_j++) 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++) 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++) 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++) begin : block_s2m_i
+			assign DAT_R[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)?
+										TDAT_R[initiator_selected_target[s2m_i]]: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++) begin : WB_M2S_assign
+			assign TADR[m2s_i] = (target_active_initiator[m2s_i] == NO_INITIATOR)?0:ADR[target_active_initiator[m2s_i]];
+			assign TDAT_W[m2s_i] = (target_active_initiator[m2s_i] == NO_INITIATOR)?0:DAT_W[target_active_initiator[m2s_i]];
+			assign TCYC[m2s_i] = (target_active_initiator[m2s_i] == NO_INITIATOR)?0:CYC[target_active_initiator[m2s_i]];
+			assign TSEL[m2s_i] = (target_active_initiator[m2s_i] == NO_INITIATOR)?0:SEL[target_active_initiator[m2s_i]];
+			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 int			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--) 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++) 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++) 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(reg[N_REQ-1:0] gnt);
+		automatic int i;
+		reg[$clog2(N_REQ)-1:0] result;
+		
+		result = 0;
+		
+		for (i=0; i<N_REQ; i++) begin
+			if (gnt[i]) begin
+				result |= i;
+			end
+		end
+	
+		return result;
+	endfunction
+
+endmodule