gateware/icE1usb-proto: Initial import

This is the project specific to the PMOD based early prototype.
It was used either with the icebreaker or the icebreaker-bitsy
board as host. Set BOARD variable appropriately during build.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
diff --git a/gateware/icE1usb-proto/.gitignore b/gateware/icE1usb-proto/.gitignore
new file mode 100644
index 0000000..24d1c97
--- /dev/null
+++ b/gateware/icE1usb-proto/.gitignore
@@ -0,0 +1,4 @@
+build-tmp
+__pycache__
+*.vcd
+.*.swp
diff --git a/gateware/icE1usb-proto/Makefile b/gateware/icE1usb-proto/Makefile
new file mode 100644
index 0000000..52f4b5a
--- /dev/null
+++ b/gateware/icE1usb-proto/Makefile
@@ -0,0 +1,43 @@
+# Project config
+PROJ=icE1usb-proto
+
+PROJ_DEPS := no2e1 no2ice40 no2misc no2usb
+PROJ_RTL_SRCS := $(addprefix rtl/, \
+	misc.v \
+	sysmgr.v \
+)
+PROJ_RTL_SRCS += $(addprefix ../common/rtl/, \
+	dfu_helper.v \
+	picorv32.v \
+	soc_base.v \
+	soc_bram.v \
+	soc_iobuf.v \
+	soc_picorv32_bridge.v \
+	soc_spram.v \
+	wb_arbiter.v \
+	wb_dma.v \
+	wb_epbuf.v \
+)
+PROJ_PREREQ = \
+	$(BUILD_TMP)/boot.hex \
+	$(NULL)
+PROJ_TOP_SRC := rtl/top.v
+PROJ_TOP_MOD := top
+
+# Target config
+BOARD ?= icebreaker
+DEVICE = up5k
+PACKAGE = sg48
+
+NEXTPNR_ARGS = --pre-pack data/clocks.py --seed 4
+
+# Include default rules
+NO2BUILD_DIR := ../build
+include $(NO2BUILD_DIR)/project-rules.mk
+
+# Custom rules
+../common/fw/boot.hex:
+	make -C ../common/fw boot.hex
+
+$(BUILD_TMP)/boot.hex: ../common/fw/boot.hex
+	cp $< $@
diff --git a/gateware/icE1usb-proto/data/clocks.py b/gateware/icE1usb-proto/data/clocks.py
new file mode 100644
index 0000000..3a6a288
--- /dev/null
+++ b/gateware/icE1usb-proto/data/clocks.py
@@ -0,0 +1,2 @@
+ctx.addClock("clk_sys", 30.72)
+ctx.addClock("clk_48m", 48)
diff --git a/gateware/icE1usb-proto/data/top-bitsy-v0.pcf b/gateware/icE1usb-proto/data/top-bitsy-v0.pcf
new file mode 100644
index 0000000..2f13dbb
--- /dev/null
+++ b/gateware/icE1usb-proto/data/top-bitsy-v0.pcf
@@ -0,0 +1,40 @@
+# E1 PHY
+set_io e1_rx_hi_p 48
+set_io e1_rx_hi_n 45
+set_io e1_rx_lo_p  4
+set_io e1_rx_lo_n  3
+set_io e1_tx_hi   46
+set_io e1_tx_lo   47
+
+set_io e1_vref_ct_pdm 32
+set_io e1_vref_p_pdm  27
+set_io e1_vref_n_pdm  34
+
+# USB
+set_io usb_dp 43
+set_io usb_dn 42
+set_io usb_pu 38
+
+# Flash
+set_io -pullup yes flash_mosi 14
+set_io -pullup yes flash_miso 17
+set_io -pullup yes flash_clk  15
+set_io -pullup yes flash_cs_n 16
+
+# Button
+set_io -pullup yes btn 10
+
+# Clock
+set_io clk_in 44
+
+set_io clk_tune_hi 28
+set_io clk_tune_lo 31
+
+# Debug UART
+set_io -pullup yes dbg_rx 18
+set_io -pullup yes dbg_tx 19
+
+# RGB LEDs
+set_io rgb[0] 39
+set_io rgb[1] 40
+set_io rgb[2] 41
diff --git a/gateware/icE1usb-proto/data/top-icebreaker.pcf b/gateware/icE1usb-proto/data/top-icebreaker.pcf
new file mode 100644
index 0000000..1a745a7
--- /dev/null
+++ b/gateware/icE1usb-proto/data/top-icebreaker.pcf
@@ -0,0 +1,41 @@
+# E1 PHY
+set_io e1_rx_hi_p 43
+set_io e1_rx_hi_n 36
+set_io e1_rx_lo_p 42
+set_io e1_rx_lo_n 38
+set_io e1_tx_hi   32
+set_io e1_tx_lo   31
+
+set_io e1_vref_ct_pdm 46
+set_io e1_vref_p_pdm  34
+set_io e1_vref_n_pdm  28
+
+# USB
+set_io usb_dp 4
+set_io usb_dn 3
+set_io usb_pu 2
+set_io -nowarn usb_vsense 48
+
+# Flash
+set_io -pullup yes flash_mosi 14
+set_io -pullup yes flash_miso 17
+set_io -pullup yes flash_clk  15
+set_io -pullup yes flash_cs_n 16
+
+# Button
+set_io -pullup yes btn 10
+
+# Clock
+set_io clk_in 44
+
+set_io clk_tune_hi 47
+set_io clk_tune_lo 45
+
+# Debug UART
+set_io -pullup yes dbg_rx 6
+set_io -pullup yes dbg_tx 9
+
+# RGB LEDs
+set_io rgb[0] 39
+set_io rgb[1] 40
+set_io rgb[2] 41
diff --git a/gateware/icE1usb-proto/doc/mem-map.md b/gateware/icE1usb-proto/doc/mem-map.md
new file mode 100644
index 0000000..87407a6
--- /dev/null
+++ b/gateware/icE1usb-proto/doc/mem-map.md
@@ -0,0 +1,226 @@
+icE1usb-proto SoC Memory Map
+============================
+
+Overview
+--------
+
+| Base        | Size             | Description     | IP Core doc
+|-------------|------------------|-----------------|-------------
+|`0x00000000` | `0x00400` (1k)   | Boot ROM        |
+|`0x00020000` | `0x10000` (64k)  | Main SRAM       |
+|`0x80000000` |                  | Flash SPI       | [`no2ice40/ice40_spi_wb`](../../cores/no2ice40/doc/ice40_spi_wb.md)
+|`0x81000000` |                  | Debug UART      | [`no2misc/uart_wb`](../../cores/no2misc/doc/uart_wb.md)
+|`0x82000000` |                  | RGB LED         | [`no2ice40/ice40_rgb_wb`](../../cores/no2ice40/doc/ice40_rgb_wb.md)
+|`0x83000000` |                  | USB core        | [`no2usb`](../../cores/no2usb/doc/mem-map.md)
+|`0x84000000` | `0x01000` (4k)   | USB data buffer |
+|`0x85000000` | `0x10000` (64k)  | E1 data buffer  |
+|`0x86000000` |                  | DMA             | See below
+|`0x87000000` |                  | E1 core         | [`no2e1`](../../cores/no2e1/doc/mem-map.md). See notes below.
+|`0x88000000` |                  | Misc            | See below
+
+
+Memory
+------
+
+### `0x00000000`: Boot ROM
+
+This memory zone is initialized directly in the FPGA bitstream itself.
+It contains the bootstrap program that will load the main firmware from
+flash into the main SRAM and jump to it.
+
+
+### `0x00020000`: Main SRAM
+
+The main SoC SRAM, implemented using the UP5k SPRAMs. It can't be initialized
+in the FPGA bitstream directly, hence the need for the Boot ROM zone.
+
+
+### `0x84000000`: USB data buffer
+
+This 4k zone actually correspond to 8k of SRAM inside the USB core.
+The RX and TX buffer for the USB are distinct and are accessed independently
+depending if read or write access are made to this zone. (RX buffer can only
+be read, TX buffer can only be written). All accesses must also be 32 bit
+wide !
+
+
+### `0x85000000`: E1 data buffer
+
+This 64k buffer is reserved for E1 data and is what is used by the E1 core.
+
+Address mapping is :
+
+```text
+,---------------------------------------------------------------,
+| f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+|---------------------------------------------------------------|
+|        multi-frame        |     frame     |    timeslot       |
+'---------------------------------------------------------------'
+```
+
+and the `multi-frame` number is what is exchanged in the E1 core buffer
+descriptors.
+
+
+Custom Peripherals
+------------------
+
+### `0x86000000`: DMA
+
+Very simple DMA core allowing direct copy of data between the USB and E1 data
+buffers.
+
+#### Status (Read Only, addr `0x00`)
+
+```text
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|                             /                 | b| d| /|               len                    |
+'-----------------------------------------------------------------------------------------------'
+
+ * [   15] - b   : Busy flag
+ * [   14] - d   : Direction ( 0=E1 to USB, 1=USB to E1 )
+ * [12: 0] - len : Remaining length of the in-progress transfer ( -1 = done )
+```
+
+#### Transfer start/direction/length (Write Only, addr `0x00`)
+
+Write to this register initiate the transfer.
+
+```text
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|                             /                    | d|  /  |             len                   |
+'-----------------------------------------------------------------------------------------------'
+
+ * [   14] - d   : Direction ( 0=E1 to USB, 1=USB to E1 )
+ * [11: 0] - len : Transfer length ( Number of Words - 2 )
+```
+
+#### E1 buffer offset (Write Only, addr `0x08`)
+
+```text
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|                     /                               |           e1_ofs                        |
+'-----------------------------------------------------------------------------------------------'
+
+ * [13:0] - e1_ofs
+```
+
+Word offset inside the E1 data buffer where the next transfer will start
+
+#### USB buffer offset (Write Only, addr `0x0C`)
+
+```text
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|                              /                                  |        usb_ofs              |
+'-----------------------------------------------------------------------------------------------'
+
+ * [9:0] - usb_ofs
+```
+
+Word offset inside the USB End Point buffer where the next transfer will start
+
+
+### `0x87000000`: E1 core
+
+Refer to the [`no2e1` core documentation](../../cores/no2e1/doc/mem-map.md)
+for register description.
+
+The core instanciated here has a 1 single channel containing both
+RX and TX units.
+
+
+### `0x88000000`: Misc
+
+Collection of small auxiliary peripherals.
+
+#### Boot (Write Only, addr `0x00`)
+
+```text
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|                                         /                                            | x| sel |
+'-----------------------------------------------------------------------------------------------'
+
+ * [   2] - d   : boot eXecute
+ * [11:0] - sel : boot select
+```
+
+Write to this register with bit 2 set will trigger a FPGA reload of the selected image.
+
+#### E1 tick channel 0 (Read Only, addr `0x04`)
+
+```text
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|                  tx_tick                      |                   rx_tick                     |
+'-----------------------------------------------------------------------------------------------'
+
+ * [31:16] - tx_tick
+ * [15: 0] - rx_tick
+```
+
+An internal counter is incremented at every bit received/transmitted by the corresponding E1
+channel. That counter value is then captured at every USB Start-of-Frame packet and the last
+captured value made available here.
+
+#### Time (Read Only, addr `0x07`)
+
+```text
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|                                             time                                              |
+'-----------------------------------------------------------------------------------------------'
+
+ * [31:0] - time
+```
+
+32 bit counter incremented at the system clock rate ( 30.72 MHz )
+
+
+#### PDM (Read/Write, addr `0x08-0x0f`)
+
+This exposes configurable voltages ( DACs ). Some channels are 12 bits, some are 8 bits.
+
+```text
+12 bits:
+
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|en|                        /                               |            value                  |
+'-----------------------------------------------------------------------------------------------'
+
+ * [31  ] - enable (output tristated if 0)
+ * [11:0] - value
+
+
+ 8 bits:
+
+,-----------------------------------------------------------------------------------------------,
+|31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0|
+|-----------------------------------------------------------------------------------------------|
+|en|                               /                                    |      value            |
+'-----------------------------------------------------------------------------------------------'
+
+ * [31  ] - enable (output tristated if 0)
+ * [11:0] - value
+```
+
+Channels :
+
+  * `0`: Clock tune ( low ) [ 12 bits ]
+  * `1`: Clock tune ( high ) [ 12 bits ]
+  * `2`: E1 RX Negative bias [ 8 bits ]
+  * `3`: E1 RX Positive bias [ 8 bits ]
+  * `4`: E1 RX Center Tap bias [ 8 bits ]
diff --git a/gateware/icE1usb-proto/rtl/misc.v b/gateware/icE1usb-proto/rtl/misc.v
new file mode 100644
index 0000000..e7a2854
--- /dev/null
+++ b/gateware/icE1usb-proto/rtl/misc.v
@@ -0,0 +1,217 @@
+/*
+ * misc.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * Misc peripheral functions
+ *
+ * Copyright (C) 2019-2020  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: CERN-OHL-S-2.0
+ */
+
+`default_nettype none
+
+module misc (
+	// PDM outputs
+	output wire e1_vref_ct_pdm,
+	output wire e1_vref_p_pdm,
+	output wire e1_vref_n_pdm,
+
+	output wire clk_tune_hi,
+	output wire clk_tune_lo,
+
+	// Button
+	input  wire btn,
+
+	// Ticks
+	input  wire tick_e1_rx,
+	input  wire tick_e1_tx,
+	input  wire tick_usb_sof,
+
+	// Reset request
+	output wire        rst_req,
+
+	// Wishbone
+	input  wire [ 7:0] wb_addr,
+	output reg  [31:0] wb_rdata,
+	input  wire [31:0] wb_wdata,
+	input  wire        wb_we,
+	input  wire        wb_cyc,
+	output reg         wb_ack,
+
+	// Clock / Reset
+	input  wire clk,
+	input  wire rst
+);
+
+	// Signals
+	// -------
+
+	// Bus
+	wire        bus_clr;
+	reg         bus_we_boot;
+	reg  [ 1:0] bus_we_pdm_clk;
+	reg  [ 2:0] bus_we_pdm_e1;
+
+	// Counters
+	reg  [15:0] cnt_e1_rx;
+	reg  [15:0] cap_e1_rx;
+	reg  [15:0] cnt_e1_tx;
+	reg  [15:0] cap_e1_tx;
+	reg  [31:0] cnt_time;
+
+	// PDM
+	reg  [12:0] pdm_clk[0:1];
+	reg  [ 8:0] pdm_e1[0:2];
+
+	// Boot
+	reg   [1:0] boot_sel;
+	reg         boot_now;
+
+
+	// Bus interface
+	// -------------
+
+	// Ack
+	always @(posedge clk)
+		wb_ack <= wb_cyc & ~wb_ack;
+
+	assign bus_clr = ~wb_cyc | wb_ack;
+
+	// Write enables
+	always @(posedge clk)
+		if (bus_clr | ~wb_we) begin
+			bus_we_boot       <= 1'b0;
+			bus_we_pdm_clk[0] <= 1'b0;
+			bus_we_pdm_clk[1] <= 1'b0;
+			bus_we_pdm_e1[0]  <= 1'b0;
+			bus_we_pdm_e1[1]  <= 1'b0;
+			bus_we_pdm_e1[2]  <= 1'b0;
+		end else begin
+			bus_we_boot       <= wb_addr == 4'h0;
+			bus_we_pdm_clk[0] <= wb_addr == 4'h8;
+			bus_we_pdm_clk[1] <= wb_addr == 4'h9;
+			bus_we_pdm_e1[0]  <= wb_addr == 4'ha;
+			bus_we_pdm_e1[1]  <= wb_addr == 4'hb;
+			bus_we_pdm_e1[2]  <= wb_addr == 4'hc;
+		end
+
+	// Read mux
+	always @(posedge clk)
+		if (bus_clr)
+			wb_rdata <= 32'h00000000;
+		else
+			case (wb_addr[3:0])
+				4'h4:    wb_rdata <= { cap_e1_rx, cap_e1_tx };
+				4'h7:    wb_rdata <= cnt_time;
+				4'h8:    wb_rdata <= { pdm_clk[0][12], 19'h00000, pdm_clk[0][11:0] };
+				4'h9:    wb_rdata <= { pdm_clk[1][12], 19'h00000, pdm_clk[1][11:0] };
+				4'ha:    wb_rdata <= {  pdm_e1[0][8], 23'h000000,  pdm_e1[0][ 7:0] };
+				4'hb:    wb_rdata <= {  pdm_e1[1][8], 23'h000000,  pdm_e1[1][ 7:0] };
+				4'hc:    wb_rdata <= {  pdm_e1[2][8], 23'h000000,  pdm_e1[2][ 7:0] };
+				default: wb_rdata <= 32'hxxxxxxxx;
+			endcase
+
+
+	// Counters
+	// --------
+
+	// E1 ticks
+	always @(posedge clk or posedge rst)
+		if (rst)
+			cnt_e1_rx <= 16'h0000;
+		else if (tick_e1_rx)
+			cnt_e1_rx <= cnt_e1_rx + 1;
+
+	always @(posedge clk or posedge rst)
+		if (rst)
+			cnt_e1_tx <= 16'h0000;
+		else if (tick_e1_tx)
+			cnt_e1_tx <= cnt_e1_tx + 1;
+
+	always @(posedge clk)
+		if (tick_usb_sof) begin
+			cap_e1_rx <= cnt_e1_rx;
+			cap_e1_tx <= cnt_e1_tx;
+		end
+
+	// Time counter
+	always @(posedge clk)
+		if (rst)
+			cnt_time <= 32'h00000000;
+		else
+			cnt_time <= cnt_time + 1;
+
+
+	// PDM outputs
+	// -----------
+
+	// Registers
+	always @(posedge clk or posedge rst)
+		if (rst) begin
+			pdm_clk[0] <= 0; // 13'h1800;
+			pdm_clk[1] <= 0; // 13'h1800;
+			pdm_e1[0]  <= 0; // 9'h190;
+			pdm_e1[1]  <= 0; // 9'h190;
+			pdm_e1[2]  <= 0; // 9'h190;
+		end else begin
+			if (bus_we_pdm_clk[0])  pdm_clk[0] <= { wb_wdata[31], wb_wdata[11:0] };
+			if (bus_we_pdm_clk[1])  pdm_clk[1] <= { wb_wdata[31], wb_wdata[11:0] };
+			if (bus_we_pdm_e1[0])   pdm_e1[0]  <= { wb_wdata[31], wb_wdata[ 7:0] };
+			if (bus_we_pdm_e1[1])   pdm_e1[1]  <= { wb_wdata[31], wb_wdata[ 7:0] };
+			if (bus_we_pdm_e1[2])   pdm_e1[2]  <= { wb_wdata[31], wb_wdata[ 7:0] };
+		end
+
+	// PDM cores
+	pdm #(
+		.WIDTH(12),
+		.PHY("ICE40"),
+		.DITHER("YES")
+	) pdm_clk_I[1:0] (
+		.pdm    ({ clk_tune_hi,      clk_tune_lo      }),
+		.cfg_val({ pdm_clk[1][11:0], pdm_clk[0][11:0] }),
+		.cfg_oe ({ pdm_clk[1][12],   pdm_clk[0][12]   }),
+		.clk    (clk),
+		.rst    (rst)
+	);
+
+	pdm #(
+		.WIDTH(8),
+		.PHY("ICE40"),
+		.DITHER("NO")
+	) pdm_e1_I[2:0] (
+		.pdm    ({ e1_vref_ct_pdm, e1_vref_p_pdm,  e1_vref_n_pdm  }),
+		.cfg_val({ pdm_e1[2][7:0], pdm_e1[1][7:0], pdm_e1[0][7:0] }),
+		.cfg_oe ({ pdm_e1[2][8],   pdm_e1[1][8],   pdm_e1[0][8]   }),
+		.clk    (clk),
+		.rst    (rst)
+	);
+
+
+	// DFU / Reboot
+	// ------------
+
+	always @(posedge clk or posedge rst)
+		if (rst) begin
+			boot_now <= 1'b0;
+			boot_sel <= 2'b00;
+		end else if (bus_we_boot) begin
+			boot_now <= wb_wdata[2];
+			boot_sel <= wb_wdata[1:0];
+		end
+
+	dfu_helper #(
+		.TIMER_WIDTH(26),
+		.BTN_MODE(3),
+		.DFU_MODE(0)
+	) dfu_I (
+		.boot_sel(boot_sel),
+		.boot_now(boot_now),
+		.btn_pad (btn),
+		.btn_val (),
+		.rst_req (rst_req),
+		.clk     (clk),
+		.rst     (rst)
+	);
+
+endmodule // misc
diff --git a/gateware/icE1usb-proto/rtl/sysmgr.v b/gateware/icE1usb-proto/rtl/sysmgr.v
new file mode 100644
index 0000000..6ae9721
--- /dev/null
+++ b/gateware/icE1usb-proto/rtl/sysmgr.v
@@ -0,0 +1,100 @@
+/*
+ * sysmgr.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * System Clock / Reset generation
+ *
+ * Copyright (C) 2019-2020  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: CERN-OHL-S-2.0
+ */
+
+`default_nettype none
+
+module sysmgr (
+	input  wire clk_in,
+	input  wire rst_in,
+	output wire clk_sys,
+	output wire rst_sys,
+	output wire clk_48m,
+	output wire rst_48m
+);
+
+	// Signals
+	wire pll_lock;
+	wire pll_reset_n;
+
+	wire clk_30m72_i;
+	wire rst_30m72_i;
+	wire clk_48m_i;
+	reg  rst_48m_i;
+
+	reg [3:0] rst_cnt;
+
+	// Global input buffer for 30.72 MHz clock
+	SB_GB_IO #(
+		.PIN_TYPE(6'b000001),
+	) gb_in (
+		.PACKAGE_PIN(clk_in),
+		.GLOBAL_BUFFER_OUTPUT(clk_30m72_i),
+	);
+
+	// PLL instance
+	SB_PLL40_CORE #(
+		.DIVR(4'b0000),
+		.DIVF(7'b0011000),
+		.DIVQ(3'b100),
+		.FILTER_RANGE(3'b011),
+		.FEEDBACK_PATH("SIMPLE"),
+		.DELAY_ADJUSTMENT_MODE_FEEDBACK("FIXED"),
+		.FDA_FEEDBACK(4'b0000),
+		.SHIFTREG_DIV_MODE(2'b00),
+		.PLLOUT_SELECT("GENCLK"),
+		.ENABLE_ICEGATE(1'b0),
+	) pll_I (
+		.REFERENCECLK(clk_30m72_i),
+		.PLLOUTCORE(),
+		.PLLOUTGLOBAL(clk_48m_i),
+		.EXTFEEDBACK(1'b0),
+		.DYNAMICDELAY(8'h00),
+		.RESETB(pll_reset_n),
+		.BYPASS(1'b0),
+		.LATCHINPUTVALUE(1'b0),
+		.LOCK(pll_lock),
+		.SDI(1'b0),
+		.SDO(),
+		.SCLK(1'b0)
+	);
+
+	assign clk_sys = clk_30m72_i;
+	assign clk_48m = clk_48m_i;
+
+	// PLL reset generation
+	assign pll_reset_n = ~rst_in;
+
+	// Logic reset generation
+	always @(posedge clk_30m72_i or negedge pll_lock)
+		if (!pll_lock)
+			rst_cnt <= 4'h0;
+		else if (~rst_cnt[3])
+			rst_cnt <= rst_cnt + 1;
+
+	assign rst_30m72_i = ~rst_cnt[3];
+
+	always @(posedge clk_48m or posedge rst_30m72_i)
+		if (rst_30m72_i)
+			rst_48m_i <= 1'b1;
+		else
+			rst_48m_i <= 1'b0;
+
+	SB_GB rst_sys_gbuf_I (
+		.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_30m72_i),
+		.GLOBAL_BUFFER_OUTPUT(rst_sys)
+	);
+
+	SB_GB rst_48m_gbuf_I (
+		.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_48m_i),
+		.GLOBAL_BUFFER_OUTPUT(rst_48m)
+	);
+
+endmodule // sysmgr
diff --git a/gateware/icE1usb-proto/rtl/top.v b/gateware/icE1usb-proto/rtl/top.v
new file mode 100644
index 0000000..7cefeac
--- /dev/null
+++ b/gateware/icE1usb-proto/rtl/top.v
@@ -0,0 +1,202 @@
+/*
+ * top.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * Top-level for the icE1usb icebreaker/bitsy based prototypes
+ *
+ * Copyright (C) 2019-2020  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: CERN-OHL-S-2.0
+ */
+
+`default_nettype none
+
+module top (
+	// E1 PHY
+	input  wire e1_rx_hi_p,
+//	input  wire e1_rx_hi_n,
+	input  wire e1_rx_lo_p,
+//	input  wire e1_rx_lo_n,
+
+	output wire e1_tx_hi,
+	output wire e1_tx_lo,
+
+	output wire e1_vref_ct_pdm,
+	output wire e1_vref_p_pdm,
+	output wire e1_vref_n_pdm,
+
+	// USB
+	inout  wire usb_dp,
+	inout  wire usb_dn,
+	output wire usb_pu,
+
+	// Flash
+	inout  wire flash_mosi,
+	inout  wire flash_miso,
+	inout  wire flash_clk,
+	inout  wire flash_cs_n,
+
+	// Button
+	input  wire btn,
+
+	// Clock (30.72 MHz)
+	input  wire clk_in,
+	output wire clk_tune_hi,
+	output wire clk_tune_lo,
+
+	// Debug UART
+	input  wire dbg_rx,
+	output wire dbg_tx,
+
+	// RGB LEDs
+	output wire [2:0] rgb
+);
+
+	localparam integer WB_N = 1;
+
+	genvar i;
+
+
+	// Signals
+	// -------
+
+	// Flash SPI internal signals
+	wire flash_mosi_i,  flash_miso_i,  flash_clk_i;
+	wire flash_mosi_o,  flash_miso_o,  flash_clk_o;
+	wire flash_mosi_oe, flash_miso_oe, flash_clk_oe;
+	wire flash_csn_o;
+
+	// Peripheral wishbone
+	wire     [15:0] wb_addr;
+	wire     [31:0] wb_rdata [0:WB_N-1];
+	wire     [31:0] wb_wdata;
+	wire     [ 3:0] wb_wmsk;
+	wire            wb_we;
+	wire [WB_N-1:0] wb_cyc;
+	wire [WB_N-1:0] wb_ack;
+
+	wire [(WB_N*32)-1:0] wb_rdata_flat;
+
+	// Ticks
+	wire tick_e1_rx;
+	wire tick_e1_tx;
+	wire tick_usb_sof;
+
+	// Clocks / Reset
+	wire rst_req;
+
+	wire clk_sys;
+	wire rst_sys;
+	wire clk_48m;
+	wire rst_48m;
+
+
+	// SoC base
+	// --------
+
+	// Instance
+	soc_base #(
+		.WB_N(WB_N),
+		.E1_N(1),
+		.E1_UNIT_HAS_RX(1'b1),
+		.E1_UNIT_HAS_TX(1'b1),
+		.E1_LIU(0)
+	) soc_I (
+		.e1_rx_hi_p   (e1_rx_hi_p),
+//		.e1_rx_hi_n   (e1_rx_hi_n),
+		.e1_rx_lo_p   (e1_rx_lo_p),
+//		.e1_rx_lo_n   (e1_rx_lo_n),
+		.e1_tx_hi     (e1_tx_hi),
+		.e1_tx_lo     (e1_tx_lo),
+		.e1_rx_data   (),
+		.e1_rx_clk    (),
+		.e1_tx_data   (),
+		.e1_tx_clk    (),
+		.usb_dp       (usb_dp),
+		.usb_dn       (usb_dn),
+		.usb_pu       (usb_pu),
+		.flash_mosi_i (flash_mosi_i),
+		.flash_mosi_o (flash_mosi_o),
+		.flash_mosi_oe(flash_mosi_oe),
+		.flash_miso_i (flash_miso_i),
+		.flash_miso_o (flash_miso_o),
+		.flash_miso_oe(flash_miso_oe),
+		.flash_clk_i  (flash_clk_i),
+		.flash_clk_o  (flash_clk_o),
+		.flash_clk_oe (flash_clk_oe),
+		.flash_csn_o  (flash_csn_o),
+		.dbg_rx       (dbg_rx),
+		.dbg_tx       (dbg_tx),
+		.rgb          (rgb),
+		.wb_m_addr    (wb_addr),
+		.wb_m_rdata   (wb_rdata_flat),
+		.wb_m_wdata   (wb_wdata),
+		.wb_m_wmsk    (wb_wmsk),
+		.wb_m_we      (wb_we),
+		.wb_m_cyc     (wb_cyc),
+		.wb_m_ack     (wb_ack),
+		.tick_e1_rx   (tick_e1_rx),
+		.tick_e1_tx   (tick_e1_tx),
+		.tick_usb_sof (tick_usb_sof),
+		.clk_sys      (clk_sys),
+		.rst_sys      (rst_sys),
+		.clk_48m      (clk_48m),
+		.rst_48m      (rst_48m)
+	);
+
+	// WB read data flattening
+	for (i=0; i<WB_N; i=i+1)
+		assign wb_rdata_flat[i*32+:32] = wb_rdata[i];
+
+	// SPI IO
+	SB_IO #(
+		.PIN_TYPE(6'b101001),
+		.PULLUP(1'b1)
+	) spi_io_I[2:0] (
+		.PACKAGE_PIN  ({flash_mosi,    flash_miso,    flash_clk   }),
+		.OUTPUT_ENABLE({flash_mosi_oe, flash_miso_oe, flash_clk_oe}),
+		.D_OUT_0      ({flash_mosi_o,  flash_miso_o,  flash_clk_o }),
+		.D_IN_0       ({flash_mosi_i,  flash_miso_i,  flash_clk_i })
+	);
+
+	assign flash_cs_n = flash_csn_o;
+
+
+	// Misc [0]
+	// ----
+
+	misc misc_I (
+		.e1_vref_ct_pdm(e1_vref_ct_pdm),
+		.e1_vref_p_pdm (e1_vref_p_pdm),
+		.e1_vref_n_pdm (e1_vref_n_pdm),
+		.clk_tune_hi   (clk_tune_hi),
+		.clk_tune_lo   (clk_tune_lo),
+		.btn           (btn),
+		.tick_e1_rx    (tick_e1_rx),
+		.tick_e1_tx    (tick_e1_tx),
+		.tick_usb_sof  (tick_usb_sof),
+		.rst_req       (rst_req),
+		.wb_addr       (wb_addr[7:0]),
+		.wb_rdata      (wb_rdata[0]),
+		.wb_wdata      (wb_wdata),
+		.wb_we         (wb_we),
+		.wb_cyc        (wb_cyc[0]),
+		.wb_ack        (wb_ack[0]),
+		.clk           (clk_sys),
+		.rst           (rst_sys)
+	);
+
+
+	// Clock / Reset
+	// -------------
+
+	sysmgr sys_mgr_I (
+		.clk_in (clk_in),
+		.rst_in (rst_req),
+		.clk_sys(clk_sys),
+		.rst_sys(rst_sys),
+		.clk_48m(clk_48m),
+		.rst_48m(rst_48m)
+	);
+
+endmodule // top