Bladeren bron

cores/ice40: Replace in-tree core with submodule

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 4 jaren geleden
bovenliggende
commit
758485e554

+ 3 - 0
.gitmodules

@@ -7,3 +7,6 @@
 [submodule "cores/no2usb"]
 	path = cores/no2usb
 	url = https://github.com/no2fpga/no2usb.git
+[submodule "cores/no2ice40"]
+	path = cores/no2ice40
+	url = https://github.com/no2fpga/no2ice40.git

+ 0 - 4
cores/ice40/Makefile

@@ -1,4 +0,0 @@
-CORE := ice40
-
-NO2BUILD_DIR ?= $(abspath ../../build)
-include $(NO2BUILD_DIR)/core-rules.mk

+ 0 - 7
cores/ice40/README.md

@@ -1,7 +0,0 @@
-iCE40
-=====
-
-This contains a collection of utility cores specifically targetting the
-lattice iCE40 FPGA family.
-
-These cores are licensed under the BSD 3-clause licence (see LICENSE.bsd)

+ 0 - 17
cores/ice40/no2core.mk

@@ -1,17 +0,0 @@
-CORE := no2ice40
-
-RTL_SRCS_no2ice40 := $(addprefix rtl/, \
-	ice40_ebr.v \
-	ice40_spram_gen.v \
-	ice40_iserdes.v \
-	ice40_oserdes.v \
-	ice40_serdes_crg.v \
-	ice40_serdes_dff.v \
-	ice40_serdes_sync.v \
-)
-
-TESTBENCHES_no2ice40 := \
-	ice40_ebr_tb \
-	$(NULL)
-
-include $(NO2BUILD_DIR)/core-magic.mk

+ 0 - 220
cores/ice40/rtl/ice40_ebr.v

@@ -1,220 +0,0 @@
-/*
- * ice40_ebr.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-
-module ice40_ebr #(
-	parameter integer READ_MODE  = 0,	/* 0 =  256x16, 1 =  512x8 */
-	parameter integer WRITE_MODE = 0,	/* 2 = 1024x4,  3 = 2048x2 */
-	parameter integer MASK_WORKAROUND = 0,
-	parameter integer NEG_WR_CLK = 0,
-	parameter integer NEG_RD_CLK = 0,
-	parameter INIT_FILE = "",
-
-	// auto
-	parameter integer WAW = 8 + WRITE_MODE,
-	parameter integer WDW = 16 / (1 << WRITE_MODE),
-	parameter integer RAW = 8 + READ_MODE,
-	parameter integer RDW = 16 / (1 << READ_MODE)
-)(
-	// Write
-	input  wire [WAW-1:0] wr_addr,
-	input  wire [WDW-1:0] wr_data,
-	input  wire [WDW-1:0] wr_mask,
-	input  wire           wr_ena,
-	input  wire           wr_clk,
-
-	// Read
-	input  wire [RAW-1:0] rd_addr,
-	output wire [RDW-1:0] rd_data,
-	input  wire           rd_ena,
-	input  wire           rd_clk
-);
-
-	genvar i;
-
-	// Constants
-	// ---------
-
-	localparam integer WRITE_MODE_RAM = MASK_WORKAROUND ? 0 : WRITE_MODE;
-
-	localparam integer RDO = (1 << READ_MODE) >> 2;
-	localparam integer WDO = (1 << WRITE_MODE_RAM) >> 2;
-
-
-	// Functions
-	// ---------
-
-	function [15:0] bitrev16 (input [15:0] sig);
-		bitrev16 = {
-			sig[15], sig[7], sig[11], sig[3], sig[13], sig[5], sig[9], sig[1],
-			sig[14], sig[6], sig[10], sig[2], sig[12], sig[4], sig[8], sig[0]
-		};
-	endfunction
-
-
-	// Signals
-	// -------
-
-	// Raw RAM
-	wire [10:0] ram_wr_addr;
-	wire [15:0] ram_wr_data;
-	wire [15:0] ram_wr_mask;
-
-	wire [10:0] ram_rd_addr;
-	wire [15:0] ram_rd_data;
-
-
-	// Read mapping
-	// ------------
-
-	wire [15:0] rd_data_i;
-
-	assign { ram_rd_addr[7:0], ram_rd_addr[8], ram_rd_addr[9], ram_rd_addr[10] } = { rd_addr, {(3-READ_MODE){1'b0}} };
-
-	assign rd_data_i = bitrev16({ {RDO{1'b0}}, ram_rd_data[15:RDO] });
-	assign rd_data = rd_data_i[RDW-1:0];
-
-
-	// Write mapping
-	// -------------
-
-	generate
-		if ((WRITE_MODE == 0) | (MASK_WORKAROUND == 0) ) begin
-			// Normal Mapping rule
-			wire [15:0] wr_data_i = bitrev16({ {(16-WDW){1'b0}}, wr_data });
-			wire [15:0] wr_mask_i = bitrev16({ {(16-WDW){1'b0}}, wr_mask });
-
-			assign ram_wr_data = { wr_data_i[15-WDO:0], {WDO{1'b0}} };
-			assign ram_wr_mask = { wr_mask_i[15-WDO:0], {WDO{1'b0}} };
-			assign { ram_wr_addr[7:0], ram_wr_addr[8], ram_wr_addr[9], ram_wr_addr[10] } = { wr_addr, {(3-WRITE_MODE){1'b0}} };
-
-		end else begin
-			// We want mask support for non x16 mode
-			// To do this we have to stay in x16 mode and manually handle the
-			// write width adaptation
-			wire [15:0] submask;
-
-			assign ram_wr_data = bitrev16( {(1<<WRITE_MODE){wr_data}} );
-			assign ram_wr_mask = bitrev16( {(1<<WRITE_MODE){wr_mask}} | submask );
-			assign ram_wr_addr = { 3'b000, wr_addr[WAW-1:WAW-8] };
-
-			for (i=0; i<16; i=i+1)
-				assign submask[i] = !((i >> (4-WRITE_MODE)) == wr_addr[WRITE_MODE-1:0]);
-		end
-	endgenerate
-
-
-	// Memory block
-	// ------------
-
-	generate
-		if ((NEG_RD_CLK == 0) && (NEG_WR_CLK == 0))
-			SB_RAM40_4K #(
-				.INIT_FILE(INIT_FILE),
-				.WRITE_MODE(WRITE_MODE_RAM),
-				.READ_MODE(READ_MODE)
-			) ebr_I (
-				.RDATA(ram_rd_data),
-				.RADDR(ram_rd_addr),
-				.RCLK(rd_clk),
-				.RCLKE(rd_ena),
-				.RE(1'b1),
-				.WDATA(ram_wr_data),
-				.WADDR(ram_wr_addr),
-				.MASK(ram_wr_mask),
-				.WCLK(wr_clk),
-				.WCLKE(wr_ena),
-				.WE(1'b1)
-			);
-
-		else if ((NEG_RD_CLK != 0) && (NEG_WR_CLK == 0))
-			SB_RAM40_4KNR #(
-				.INIT_FILE(INIT_FILE),
-				.WRITE_MODE(WRITE_MODE_RAM),
-				.READ_MODE(READ_MODE)
-			) ebr_I (
-				.RDATA(ram_rd_data),
-				.RADDR(ram_rd_addr),
-				.RCLKN(rd_clk),
-				.RCLKE(rd_ena),
-				.RE(1'b1),
-				.WDATA(ram_wr_data),
-				.WADDR(ram_wr_addr),
-				.MASK(ram_wr_mask),
-				.WCLK(wr_clk),
-				.WCLKE(wr_ena),
-				.WE(1'b1)
-			);
-
-		else if ((NEG_RD_CLK == 0) && (NEG_WR_CLK != 0))
-			SB_RAM40_4KNW #(
-				.INIT_FILE(INIT_FILE),
-				.WRITE_MODE(WRITE_MODE_RAM),
-				.READ_MODE(READ_MODE)
-			) ebr_I (
-				.RDATA(ram_rd_data),
-				.RADDR(ram_rd_addr),
-				.RCLK(rd_clk),
-				.RCLKE(rd_ena),
-				.RE(1'b1),
-				.WDATA(ram_wr_data),
-				.WADDR(ram_wr_addr),
-				.MASK(ram_wr_mask),
-				.WCLKN(wr_clk),
-				.WCLKE(wr_ena),
-				.WE(1'b1)
-			);
-
-		else if ((NEG_RD_CLK != 0) && (NEG_WR_CLK != 0))
-			SB_RAM40_4KNRNW #(
-				.INIT_FILE(INIT_FILE),
-				.WRITE_MODE(WRITE_MODE_RAM),
-				.READ_MODE(READ_MODE)
-			) ebr_I (
-				.RDATA(ram_rd_data),
-				.RADDR(ram_rd_addr),
-				.RCLKN(rd_clk),
-				.RCLKE(rd_ena),
-				.RE(1'b1),
-				.WDATA(ram_wr_data),
-				.WADDR(ram_wr_addr),
-				.MASK(ram_wr_mask),
-				.WCLKN(wr_clk),
-				.WCLKE(wr_ena),
-				.WE(1'b1)
-			);
-
-	endgenerate
-
-endmodule

+ 0 - 302
cores/ice40/rtl/ice40_iserdes.v

@@ -1,302 +0,0 @@
-/*
- * ice40_iserdes.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-
-module ice40_iserdes #(
-	parameter EDGE_SEL  = "SINGLE_POS",	// "SINGLE_POS" / "SINGLE_NEG" / "DUAL_POS" / "DUAL_POS_NEG"
-	parameter PHASE_SEL = "STATIC",		// "STATIC" / "DYNAMIC"
-	parameter integer PHASE = 0,
-	parameter integer SERDES_GRP = 0
-)(
-	input  wire [1:0] d,
-	output wire [3:0] q,
-	input  wire       edge_sel,
-	input  wire [1:0] phase_sel,
-	input  wire       sync,
-	input  wire       clk_1x,
-	input  wire       clk_4x
-);
-
-	genvar i, j;
-
-	/* 				 0	 1
-	 * SINGLE_POS	POS	 /
-	 * SINGLE_NEG	NEG  /
-	 * DUAL_POS		POS  /
-	 * DUAL_POS_NEG	POS	NEG
-	 */
-
-	// FIXME: The DUAL_POS_NEG mode would need a negative edge sync signal as
-	// well
-
-
-	// Signals
-	// -------
-
-	wire [3:0] shift_in[0:1];
-	wire [3:0] shift_out[0:1];
-
-	wire [3:0] fcap_in[0:1];
-	wire [3:0] fcap_out[0:1];
-
-
-	// Fast paths
-	// ----------
-
-	// - For the "SINGLE_{POS,NEG}", we only have a single path
-	// - For the "DUAL_POS_POS" case it's a single path as well with a pre-sel
-	//   mux. If dynamic phase is also enabled, this option will have an added
-	//   delay (because need for mux between path and between phase)
-	// - For the "DUAL_POS_NEG" case, we have two independent paths
-
-	generate
-		for (j=0; j<2; j=j+1) begin
-			if ((j == 0) || (EDGE_SEL == "DUAL_POS_NEG"))
-			begin : fp
-				localparam IS_NEG = (EDGE_SEL == "SINGLE_NEG") || (j == 1);
-				wire edge_active;
-				wire din_mux;
-				wire din;
-
-				// Edge Select
-				// -----------
-
-				assign edge_active = (EDGE_SEL != "DUAL_POS_NEG") || (edge_sel == j);
-
-				if (EDGE_SEL == "DUAL_POS_POS") begin
-					// Need a pre-mux
-					(* dont_touch *)
-					SB_LUT4 #(
-						.LUT_INIT(16'hFC30)
-					) lut_edgemux_I (
-						.I0(1'b0),
-						.I1(edge_sel),
-						.I2(d[0]),
-						.I3(d[1]),	// Fast Path for the neg-edge
-						.O(din_mux)
-					);
-
-					if (PHASE_SEL == "DYNAMIC")
-						// If we have dynamic phase, we need the added stage
-						// for timing
-						ice40_serdes_dff #(
-							.NEG(IS_NEG),
-							.SERDES_GRP( (SERDES_GRP << 8) | 'h4b0 | (j << 4) )
-						) dff_edgemux_I (
-							.d(din_mux),
-							.q(din),
-							.c(clk_4x)
-						);
-					else
-						// This mux can be packed with the first shift
-						// register stage
-						assign din = din_mux;
-
-				end else begin
-					// Directly from IOB signal
-					assign din = d[j];
-				end
-
-
-				// Shifter
-				// -------
-
-				assign shift_in[j] = { shift_out[j][2:0], din };
-
-				for (i=0; i<4; i=i+1)
-				begin
-					ice40_serdes_dff #(
-						.NEG(IS_NEG),
-						.SERDES_GRP( (SERDES_GRP << 8) | 'h4a0 | (j << 4) | i )
-					) dff_shift_I (
-						.d(shift_in[j][i]),
-						.q(shift_out[j][i]),
-						.c(clk_4x)
-					);
-				end
-
-
-				// Fast Capture
-				// ------------
-
-				// If we have dynamic phase selection, apply the LSB here
-				if (PHASE_SEL == "DYNAMIC")
-					assign fcap_in[j] = edge_active ? (phase_sel[0] ? shift_out[j] : shift_in[j]) : 4'h0;
-				else
-					assign fcap_in[j] = edge_active ? shift_out[j] : 4'h0;
-
-				// Register
-				for (i=0; i<4; i=i+1)
-				begin
-					ice40_serdes_dff #(
-						.NEG(IS_NEG),
-						.ENA(1),
-						.SERDES_GRP( (SERDES_GRP << 8) | 'h490 | (j << 4) | i )
-					) dff_shift_I (
-						.d(fcap_in[j][i]),
-						.q(fcap_out[j][i]),
-						.e(sync),
-						.c(clk_4x)
-					);
-				end
-			end
-			else
-			begin
-				// Dummy
-				assign fcap_out[j]  = 4'h0;
-			end
-		end
-	endgenerate
-
-
-	// Slow Capture
-	// ------------
-
-	generate
-		if (PHASE_SEL == "STATIC")
-		begin
-			// Static Phase
-			// - - - - - - -
-
-			wire [3+PHASE:0] scap_in;
-			wire [3+PHASE:0] scap_out;
-
-			// Input
-			if (PHASE > 0)
-				assign scap_in[3+PHASE:4] = scap_out[PHASE-1:0];
-
-			assign scap_in[3:0] = fcap_out[0] | fcap_out[1];
-
-			// Registers
-			for (i=0; i<(4+PHASE); i=i+1)
-				ice40_serdes_dff #(
-					.SERDES_GRP( (SERDES_GRP << 8) | 'h680 | i )
-				) dff_scap_I (
-					.d(scap_in[i]),
-					.q(scap_out[i]),
-					.c(clk_1x)
-				);
-
-			// Output
-			assign q = scap_out[3+PHASE:PHASE];
-		end
-		else
-		begin
-			// Dynamic Phase
-			// - - - - - - -
-
-			wire [5:0] scap_in;
-			wire [5:0] scap_out;
-
-			// Input
-			if (EDGE_SEL == "DUAL_POS_NEG")
-			begin
-
-				// Dual Edge Path
-				// - - - - - - - -
-
-				wire [1:0] scap_pre_or;
-
-				// Pre-OR
-				(* SERDES_GRP=( (SERDES_GRP << 8) | 'h680 | 6 ) *)
-				(* dont_touch *)
-				SB_LUT4 #(
-					.LUT_INIT(16'hFFF0)
-				) or_lut_2_I (
-					.I0(1'b0),
-					.I1(1'b0),
-					.I2(fcap_out[1][2]),
-					.I3(fcap_out[0][2]),
-					.O(scap_pre_or[0])
-				);
-
-				(* SERDES_GRP=( (SERDES_GRP << 8) | 'h680 | 7 ) *)
-				(* dont_touch *)
-				SB_LUT4 #(
-					.LUT_INIT(16'hFFF0)
-				) or_lut_3_I (
-					.I0(1'b0),
-					.I1(1'b0),
-					.I2(fcap_out[1][3]),
-					.I3(fcap_out[0][3]),
-					.O(scap_pre_or[1])
-				);
-
-				// Main muxes
-				(* dont_touch *)
-				SB_LUT4 #(
-					.LUT_INIT(16'hFE54)
-				) mux_lut_I[3:0] (
-					.I0(phase_sel[1]),
-					.I1(fcap_out[1][3:0]),
-					.I2(fcap_out[0][3:0]),
-					.I3({scap_out[5:4], scap_pre_or}),
-					.O(scap_in[3:0])
-				);
-
-				// Save regs
-				assign scap_in[5:4] = fcap_out[0][1:0] | fcap_out[1][1:0];
-
-			end
-			else
-			begin
-
-				// Single Edge Path
-				// - - - - - - - - -
-
-				assign scap_in = {
-					fcap_out[0][1:0],
-					phase_sel[1] ?
-						{ scap_out[5:4], fcap_out[0][3:2] } :
-						fcap_out[0][3:0]
-				};
-
-			end
-
-			// Registers
-			for (i=0; i<6; i=i+1)
-				ice40_serdes_dff #(
-					.SERDES_GRP( (SERDES_GRP << 8) | 'h680 | i )
-				) dff_scap_I (
-					.d(scap_in[i]),
-					.q(scap_out[i]),
-					.c(clk_1x)
-				);
-
-			// Output
-			assign q = scap_out[3:0];
-		end
-	endgenerate
-
-endmodule

+ 0 - 120
cores/ice40/rtl/ice40_oserdes.v

@@ -1,120 +0,0 @@
-/*
- * ice40_oserdes.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-
-module ice40_oserdes #(
-	parameter MODE = "DATA",	// "DATA" / "CLK90_2X" / "CLK90_4X"
-	parameter integer SERDES_GRP = 0
-)(
-	input  wire [3:0] d,
-	output wire [1:0] q,
-	input  wire       sync,
-	input  wire       clk_1x,
-	input  wire       clk_4x
-);
-
-	genvar i;
-
-
-	// Signals
-	// -------
-
-	wire [3:0] cap_in;
-	wire [3:0] cap_out;
-
-	wire [3:0] shift_in;
-	wire [3:0] shift_out;
-
-	wire       delay_out;
-
-
-	// Capture
-	// -------
-
-	assign cap_in = (MODE == "CLK90_2X") ? { d[1], 1'b0, d[0], 1'b0 } : d;
-
-	generate
-		for (i=0; i<4; i=i+1)
-			ice40_serdes_dff #(
-				.SERDES_GRP( (SERDES_GRP << 8) | 'h00 | i )
-			) dff_cap_I (
-				.d(cap_in[i]),
-				.q(cap_out[i]),
-				.c(clk_1x)
-			);
-	endgenerate
-
-
-	// Shifter
-	// -------
-
-	assign shift_in = sync ? cap_out : { shift_out[2:0], 1'b0 };
-
-	generate
-		for (i=0; i<4; i=i+1)
-			ice40_serdes_dff #(
-				.SERDES_GRP( (SERDES_GRP << 8) | 'h10 | i )
-			) dff_shift_I (
-				.d(shift_in[i]),
-				.q(shift_out[i]),
-				.c(clk_4x)
-			);
-	endgenerate
-
-
-	// Output
-	// ------
-
-	generate
-		if ((MODE == "CLK90_2X") || (MODE == "CLK90_4X")) begin
-			// Delay FF for falling edge
-			ice40_serdes_dff #(
-				.SERDES_GRP( (SERDES_GRP << 8) | 'h20 )
-			) dff_out_I (
-				.d(shift_out[3]),
-				.q(delay_out),
-				.c(clk_4x)
-			);
-
-			// Output depends on clock mode
-			assign q[0] = (MODE == "CLK90_2X") ? delay_out : 1'b0;
-			assign q[1] = delay_out;
-		end else begin
-			// Simple data map, fall edge output un-used
-			assign q[0] = shift_out[3];
-			assign q[1] = 1'b0;
-		end
-	endgenerate
-
-endmodule

+ 0 - 121
cores/ice40/rtl/ice40_serdes_crg.v

@@ -1,121 +0,0 @@
-/*
- * ice40_serdes_crg.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-
-module ice40_serdes_crg #(
-	parameter integer NO_CLOCK_2X = 0
-)(
-	// Input from PLL
-	input  wire clk_4x,
-	input  wire pll_lock,
-
-	// Outputs
-	output wire clk_1x,
-	output wire clk_2x,
-	output wire rst
-);
-
-	// Signals
-	// -------
-
-	// Reset
-	reg  [3:0] rst_cnt_nxt[0:15];
-	reg  [3:0] rst_cnt = 4'h8;
-	reg        rst_i;
-
-	// Clock Divider
-	reg  [1:0] clk_div;
-	wire       clk_sync_i;
-
-
-	// Reset
-	// -----
-
-	// Counter
-	initial begin : rst_init
-		integer i;
-		for (i=0; i<16; i=i+1)
-			rst_cnt_nxt[i] = i==15 ? i : (i+1);
-	end
-
-	always @(posedge clk_4x or negedge pll_lock)
-		if (~pll_lock)
-			rst_cnt <= 4'h0;
-		else
-			rst_cnt <= rst_cnt_nxt[rst_cnt];
-
-	// Final FF
-	always @(posedge clk_4x or negedge pll_lock)
-		if (~pll_lock)
-			rst_i <= 1'b1;
-		else
-			rst_i <= (rst_cnt != 4'hf);
-
-	// Buffer reset
-	SB_GB gbuf_rst_I (
-		.USER_SIGNAL_TO_GLOBAL_BUFFER(rst_i),
-		.GLOBAL_BUFFER_OUTPUT(rst)
-	);
-
-
-	// Clock Divider & Sync
-	// --------------------
-
-	// Simple counter to generate the edges
-	always @(posedge clk_4x or negedge pll_lock)
-		if (~pll_lock)
-			clk_div <= 2'b00;
-		else
-			clk_div <= clk_div + rst_cnt[3];
-
-	// Buffer clk_2x
-	generate
-		if (NO_CLOCK_2X)
-			assign clk_2x = 1'b0;
-		else
-			(* BEL="X13/Y0/gb" *)
-			SB_GB gbuf_2x_I (
-				.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_div[0]),
-				.GLOBAL_BUFFER_OUTPUT(clk_2x)
-			);
-	endgenerate
-
-	// Buffer clk_1x
-	(* BEL="X12/Y0/gb" *)
-	SB_GB gbuf_1x_I (
-		.USER_SIGNAL_TO_GLOBAL_BUFFER(clk_div[1]),
-		.GLOBAL_BUFFER_OUTPUT(clk_1x)
-	);
-
-endmodule

+ 0 - 135
cores/ice40/rtl/ice40_serdes_dff.v

@@ -1,135 +0,0 @@
-/*
- * ice40_serdes_dff.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-
-module ice40_serdes_dff #(
-	parameter integer NEG = 0,
-	parameter integer ENA = 0,
-	parameter integer RST = 0,
-	parameter integer SERDES_GRP = -1,
-	parameter SERDES_ATTR = "",
-	parameter BEL = ""
-)(
-	input  wire d,
-	output wire q,
-	input  wire e,
-	input  wire r,
-	input  wire c
-);
-	parameter TYPE = (RST ? 4 : 0) | (ENA ? 2 : 0) | (NEG ? 1 : 0);
-
-	generate
-		if (TYPE == 0)			// Simple
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFF dff_I (
-				.D(d),
-				.Q(q),
-				.C(c)
-			);
-
-		else if (TYPE == 1)		// NEG
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFFN dff_I (
-				.D(d),
-				.Q(q),
-				.C(c)
-			);
-
-		else if (TYPE == 2)		//     ENA
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFFE dff_I (
-				.D(d),
-				.Q(q),
-				.E(e),
-				.C(c)
-			);
-
-		else if (TYPE == 3)		// NEG ENA
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFFNE dff_I (
-				.D(d),
-				.Q(q),
-				.E(e),
-				.C(c)
-			);
-
-		else if (TYPE == 4)		//         RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFFR dff_I (
-				.D(d),
-				.Q(q),
-				.R(r),
-				.C(c)
-			);
-
-		else if (TYPE == 5)		// NEG     RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFFNR dff_I (
-				.D(d),
-				.Q(q),
-				.R(r),
-				.C(c)
-			);
-
-		else if (TYPE == 6)		//     ENA RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFFER dff_I (
-				.D(d),
-				.Q(q),
-				.E(e),
-				.R(r),
-				.C(c)
-			);
-
-		else if (TYPE == 7)		// NEG ENA RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
-			(* dont_touch, keep *)
-			SB_DFFNER dff_I (
-				.D(d),
-				.Q(q),
-				.E(e),
-				.R(r),
-				.C(c)
-			);
-
-	endgenerate
-
-endmodule

+ 0 - 204
cores/ice40/rtl/ice40_serdes_sync.v

@@ -1,204 +0,0 @@
-/*
- * ice40_serdes_sync.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-
-module ice40_serdes_sync #(
-	parameter integer PHASE = 0,
-	parameter integer NEG_EDGE = 0,
-	parameter integer GLOBAL_BUF = 0,
-	parameter integer LOCAL_BUF = 0,
-	parameter BEL_COL = "X12",
-	parameter BEL_ROW = "Y15",		// Ignored if using LOCAL_BUF, Y15 used in that case
-	parameter BEL_GB  = ""
-)(
-	input  wire clk_slow,
-	input  wire clk_fast,
-	input  wire rst,
-	output wire sync
-);
-	localparam BEL_BASE  = LOCAL_BUF ? { BEL_COL, "/Y15" } : { BEL_COL, "/", BEL_ROW };
-	localparam PHASE_CMP = LOCAL_BUF ? ((PHASE + 3) % 4) : PHASE;
-
-	wire [1:0] clk_samp;
-	wire [1:0] edge_det;
-	wire [1:0] edge_found;
-	wire [1:0] cnt_next;
-	wire [1:0] cnt_val;
-
-	wire [1:0] sync_next;
-	wire [1:0] sync_i;
-
-	// Double sample of the slow clock
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc7"})
-	) ff_samp0_I (
-		.d(clk_slow),
-		.q(clk_samp[0]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc6"})
-	) ff_samp1_I (
-		.d(clk_samp[0]),
-		.q(clk_samp[1]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	// Detect falling edge, then rising edge
-	assign edge_det[0] = edge_found[0] | (clk_samp[1] & ~clk_samp[0]);
-	assign edge_det[1] = edge_found[1] | (clk_samp[0] & ~clk_samp[1] & edge_found[0]);
-
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc5"})
-	) ff_edge0_I (
-		.d(edge_det[0]),
-		.q(edge_found[0]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc4"})
-	) ff_edge1_I (
-		.d(edge_det[1]),
-		.q(edge_found[1]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	// 2 bit upcounter
-	assign cnt_next[0] = cnt_val[0] ^ edge_found[1];
-	assign cnt_next[1] = cnt_val[1] ^ (cnt_val[0] & edge_found[1]);
-
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc2"})
-	) ff_cnt1_I (
-		.d(cnt_next[1]),
-		.q(cnt_val[1]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc1"})
-	) ff_cnt0_I (
-		.d(cnt_next[0]),
-		.q(cnt_val[0]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	// Final comparator
-	SB_LUT4 #(
-		.LUT_INIT(1 << ((4*PHASE_CMP)+2))
-	) lut_sync_I[1:0] (
-		.I0(1'b0),
-		.I1(edge_found[1]),
-		.I2(cnt_val[0]),
-		.I3(cnt_val[1]),
-		.O(sync_next)
-	);
-
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc3"})
-	) ff_sync1_I (
-		.d(sync_next[1]),
-		.q(sync_i[1]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	ice40_serdes_dff #(
-		.NEG(NEG_EDGE),
-		.RST(1),
-		.BEL({BEL_BASE, "/lc0"})
-	) ff_sync0_I (
-		.d(sync_next[0]),
-		.q(sync_i[0]),
-		.c(clk_fast),
-		.r(rst)
-	);
-
-	// Buffer ?
-	generate
-		if (GLOBAL_BUF)
-			(* BEL=BEL_GB *)
-			SB_GB gbuf_sync_I (
-				.USER_SIGNAL_TO_GLOBAL_BUFFER(sync_i[0]),
-				.GLOBAL_BUFFER_OUTPUT(sync)
-			);
-		else
-			assign sync = sync_i[0];
-
-		if (LOCAL_BUF) begin
-			ice40_serdes_dff #(
-				.NEG(NEG_EDGE),
-				.BEL({BEL_COL, "/Y4/lc1"}),
-				.SERDES_ATTR("sync_lbuf_bot")
-			) sync_bot (
-				.d(sync_i[0]),
-				.q(), // Output will be wired by the script
-				.c(clk_fast)
-			);
-
-			ice40_serdes_dff #(
-				.NEG(NEG_EDGE),
-				.BEL({BEL_COL, "/Y26/lc1"}),
-				.SERDES_ATTR("sync_lbuf_top")
-			) sync_top (
-				.d(sync_i[1]),
-				.q(),
-				.c(clk_fast)
-			);
-		end
-	endgenerate
-
-endmodule

+ 0 - 172
cores/ice40/rtl/ice40_spram_gen.v

@@ -1,172 +0,0 @@
-/*
- * ice40_spram_gen.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-
-module ice40_spram_gen #(
-	parameter integer ADDR_WIDTH = 15,
-	parameter integer DATA_WIDTH = 32,
-
-	// auto
-	parameter integer MASK_WIDTH = (DATA_WIDTH + 3) / 4,
-
-	parameter integer AL = ADDR_WIDTH - 1,
-	parameter integer DL = DATA_WIDTH - 1,
-	parameter integer ML = MASK_WIDTH - 1
-)(
-	input  wire [AL:0] addr,
-	output reg  [DL:0] rd_data,
-	input  wire        rd_ena,		// Write WILL corrupt read data
-	input  wire [DL:0] wr_data,
-	input  wire [ML:0] wr_mask,
-	input  wire        wr_ena,
-	input  wire        clk
-);
-
-	genvar x, y;
-
-	// Constants
-	// ---------
-
-	localparam integer ND = 1 << (ADDR_WIDTH - 14);
-	localparam integer NW = (DATA_WIDTH + 15) / 16;
-
-	localparam integer MSW = (ADDR_WIDTH > 14) ? (ADDR_WIDTH - 14) : 0;
-	localparam integer MDW = NW * 16;
-	localparam integer MMW = NW *  4;
-
-	initial
-		$display("ice40_spram_gen: (%dx%d) -> %dx%d array of SPRAM\n", (1<<ADDR_WIDTH), DATA_WIDTH, ND, NW);
-
-
-	// Signals
-	// -------
-
-	reg  [AL:0]    addr_r;
-
-	wire [13:0]    mem_addr;
-
-	wire [ND-1:0]  mem_sel;
-	wire [ND-1:0]  mem_ce;
-	wire [ND-1:0]  mem_wren;
-
-	wire [MDW-1:0] mem_do_m[0:ND-1];
-	wire [MDW-1:0] mem_do_w;
-	reg  [MDW-1:0] mem_di_w;
-	reg  [MMW-1:0] mem_dm_w;
-
-	wire [15:0]    mem_do[0:ND*NW-1];
-	wire [15:0]    mem_di[0:ND*NW-1];
-	wire [ 3:0]    mem_dm[0:ND*NW-1];
-
-
-	// Main logic
-	// ----------
-
-	// Register address for read-muxing
-	always @(posedge clk)
-		if (rd_ena)
-			addr_r <= addr;
-
-	// Address mapping
-	assign mem_addr = addr[13:0];
-
-	// Map input data to/from 16*NW bit words
-	always @(*)
-	begin : map
-		integer n, x, o;
-
-		// Map actual bits
-		for (n=0; n<MASK_WIDTH; n=n+1)
-		begin
-			// Determine position
-			x = n % NW;	// Which SPRAM
-			o = n / NW;	// Which nibble inside that SPRAM
-
-			// Map IO
-			mem_di_w[(16*x)+(4*o)+:4] =  wr_data[4*n+:4];
-			mem_dm_w[( 4*x)+   o    ] = ~wr_mask[n];
-
-			rd_data[4*n+:4] = mem_do_w[(16*x)+(4*o)+:4];
-		end
-	end
-
-	// Generate memory array
-	generate
-		// Per-depth loop
-		for (y=0; y<ND; y=y+1)
-		begin
-			// Per-width loop
-			for (x=0; x<NW; x=x+1)
-			begin
-				// IO mapping for word
-				assign mem_di[y*NW+x] = mem_di_w[x*16+:16];
-				assign mem_dm[y*NW+x] = mem_dm_w[x* 4+: 4];
-
-				assign mem_do_m[y][x*16+:16] = mem_do[y*NW+x];
-
-				// Memory element
-				SB_SPRAM256KA ram_I (
-					.ADDRESS   (mem_addr),
-					.DATAIN    (mem_di[y*NW+x]),
-					.MASKWREN  (mem_dm[y*NW+x]),
-					.WREN      (mem_wren[y]),
-					.CHIPSELECT(mem_ce[y]),
-					.CLOCK     (clk),
-					.STANDBY   (1'b0),
-					.SLEEP     (1'b0),
-					.POWEROFF  (1'b1),
-					.DATAOUT   (mem_do[y*NW+x])
-				);
-			end
-
-			// Enables
-			if (MSW == 0)
-				assign mem_sel[y] = 1'b1;
-			else
-				assign mem_sel[y] = (addr[AL:AL-MSW+1] == y);
-
-			assign mem_wren[y] = mem_sel[y] &  wr_ena;
-			assign mem_ce[y]   = mem_sel[y] & (wr_ena | rd_ena);
-
-			// Muxing
-			if (MSW == 0)
-				// Trivial case
-				assign mem_do_w = mem_do_m[0];
-			else
-				// Read side mux
-				assign mem_do_w = mem_do_m[addr_r[AL:AL-MSW+1]];
-		end
-	endgenerate
-
-endmodule

+ 0 - 138
cores/ice40/sim/ice40_ebr_tb.v

@@ -1,138 +0,0 @@
-/*
- * ice40_ebr_tb.v
- *
- * vim: ts=4 sw=4
- *
- * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
- * All rights reserved.
- *
- * BSD 3-clause, see LICENSE.bsd
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in the
- *       documentation and/or other materials provided with the distribution.
- *     * Neither the name of the <organization> nor the
- *       names of its contributors may be used to endorse or promote products
- *       derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-`default_nettype none
-`timescale 1ns/100ps
-
-module ice40_ebr_tb #(
-	parameter integer READ_MODE  = 2,	/* 0 =  256x16, 1 =  512x8 */
-	parameter integer WRITE_MODE = 1,	/* 2 = 1024x4,  3 = 2048x2 */
-	parameter integer MASK_WORKAROUND = 1
-);
-
-	// Config
-	// ------
-
-	localparam integer WAW = 8 + WRITE_MODE;
-	localparam integer WDW = 16 / (1 << WRITE_MODE);
-	localparam integer RAW = 8 + READ_MODE;
-	localparam integer RDW = 16 / (1 << READ_MODE);
-
-
-	// Signals
-	// -------
-
-	reg  [WAW-1:0] wr_addr;
-	reg  [WDW-1:0] wr_data;
-	reg  [WDW-1:0] wr_mask;
-	reg            wr_ena;
-
-	reg  [RAW-1:0] rd_addr;
-	wire [RDW-1:0] rd_data;
-	reg            rd_ena;
-
-	reg  clk = 0;
-	reg  rst = 1;
-
-
-	// Test bench
-	// ----------
-
-	// Setup recording
-	initial begin
-		$dumpfile("ice40_ebr_tb.vcd");
-		$dumpvars(0,ice40_ebr_tb);
-	end
-
-	// Reset pulse
-	initial begin
-		# 200 rst = 0;
-		# 1000000 $finish;
-	end
-
-    // Clocks
-    always #10 clk = !clk;
-
-
-
-	// DUT
-	// ---
-
-	ice40_ebr #(
-		.READ_MODE(READ_MODE),
-		.WRITE_MODE(WRITE_MODE),
-		.MASK_WORKAROUND(MASK_WORKAROUND)
-	) dut_I (
-		.wr_addr(wr_addr),
-		.wr_data(wr_data),
-		.wr_mask(wr_mask),
-		.wr_ena(wr_ena),
-		.wr_clk(clk),
-		.rd_addr(rd_addr),
-		.rd_data(rd_data),
-		.rd_ena(rd_ena),
-		.rd_clk(clk)
-	);
-
-
-	reg mode;
-
-	always @(posedge clk)
-	begin
-		// Writes
-		if (!mode) begin
-			wr_addr <= wr_addr + wr_ena;
-			wr_data <= $random;
-			wr_mask <= 8'hf0;
-			wr_ena  <= ~&wr_addr;
-			mode    <= mode ^ &wr_addr;
-		end
-
-		// Reads
-		if (mode) begin
-			rd_addr <= rd_addr + rd_ena;
-			rd_ena  <= ~&rd_addr;
-			mode    <= mode ^ &rd_addr;
-		end
-
-		// Reset
-		if (rst) begin
-			wr_addr <= 0;
-			wr_ena  <= 1'b0;
-			rd_addr <= 0;
-			rd_ena  <= 1'b0;
-			mode    <= 1'b0;
-		end
-	end
-
-endmodule

+ 0 - 460
cores/ice40/sw/serdes-nextpnr-place.py

@@ -1,460 +0,0 @@
-#!/usr/bin/env python3
-
-from collections import namedtuple
-import re
-
-
-# SerDes group numbers:
-#  [15:12] Group
-#  [11: 8] SubGroup
-#  [ 7: 4] Type
-#  [    3] n/a
-#  [ 2: 0] LC number
-#
-# SubGroups:
-#
-#  0 Data Out path 0
-#  1 Data Out path 1
-#  2 Data Out Enable
-#
-#  4 Data In  path 0
-#  5 Data In  path 1
-#  6 Data In  common
-#
-#
-# Types:
-#
-# 0 OSERDES Capture
-# 1 OSERDES Shift
-# 2 OSERDES NegEdge Delay
-#
-# 8 ISERDES Slow Capture
-# 9 ISERDES Fast Capture
-# a ISERDES Shift
-# b ISERDES PreMux
-#
-#
-# Placement priority
-#
-#        type    near
-#        2       io      Output Neg Edge delay
-#        b       io      Input Pre mux
-#        a       io      Input Shift
-#        9       'a'     Input Fast Capture
-#        1       io      Output Shift
-#        0       '1'     Output Capture
-#        8       '9's    Input Slow Capture
-#
-
-
-class BEL(namedtuple('BEL', 'x y z')):
-
-	@classmethod
-	def from_json_attr(kls, v):
-		def to_int(s):
-			return int(re.sub(r'[^\d-]+', '', s))
-		return kls(*[to_int(x) for x in v.split('/', 3)])
-
-
-class ControlGroup(namedtuple('ControlGroup', 'clk rst ena neg')):
-
-	@classmethod
-	def from_lc(kls, lc):
-		netname = lambda lc, p: lc.ports[p].net.name if (lc.ports[p].net is not None) else None
-		return kls(
-			netname(lc, 'CLK'),
-			netname(lc, 'SR'),
-			netname(lc, 'CEN'),
-			lc.params['NEG_CLK'] == '1'
-		)
-
-
-class FullCellId(namedtuple('SDGId', 'gid sid typ lc')):
-
-	@classmethod
-	def from_json_attr(kls, v):
-		return kls(
-			v >> 12,
-			(v >> 8) & 0xf,
-			(v >> 4) & 0xf,
-			(v >> 0) & 0x7
-		)
-
-
-class SerDesGroup:
-
-	def __init__(self, gid):
-		self.gid = gid
-		self.blocks = {}
-		self.io = None
-
-	def add_lc(self, lc, fcid=None):
-		# Get Full Cell ID if not provided
-		if fcid is None:
-			grp = int(lc.attrs['SERDES_GRP'], 2)
-			fcid = FullCellId.from_json_attr(grp)
-
-		# Add to the cell list
-		if (fcid.sid, fcid.typ) not in self.blocks:
-			self.blocks[(fcid.sid, fcid.typ)] = SerDesBlock(self, fcid.sid, fcid.typ)
-
-		self.blocks[(fcid.sid, fcid.typ)].add_lc(lc, fcid=fcid)
-
-	def analyze(self):
-		# Process all blocks
-		for blk in self.blocks.values():
-			# Analyze
-			blk.analyze()
-
-			# Check IO
-			if blk.io is not None:
-				if (self.io is not None) and (self.io != blk.io):
-					raise RuntimeError(f'Incompatible IO sites found in SerDes group {self.gid}: {self.io} vs {blk.io}')
-				self.io = blk.io
-
-
-class SerDesBlock:
-
-	NAMES = {
-		0x0: 'OSERDES Capture',
-		0x1: 'OSERDES Shift',
-		0x2: 'OSERDES NegEdge Delay',
-		0x8: 'ISERDES Slow Capture',
-		0x9: 'ISERDES Fast Capture',
-		0xa: 'ISERDES Shift',
-		0xb: 'ISERDES PreMux',
-	}
-
-	def __init__(self, group, sid, typ):
-		# Identity
-		self.group = group
-		self.sid = sid
-		self.typ = typ
-
-		# Container
-		self.lcs = 8 * [None]
-		self.io = None
-		self.cg = None
-
-	def __str__(self):
-		return f'SerDesBlock({self.sid:x}/{self.typ:x} {self.NAMES[self.typ]})'
-
-	def _find_io_site_for_lc(self, lc):
-		# Check in/out ports
-		for pn in [ 'I0', 'I1', 'I2', 'I3', 'O' ]:
-			n = lc.ports[pn].net
-			if (n is None) or n.name.startswith('$PACKER_'):
-				continue
-			pl = [ n.driver ] + list(n.users)
-			for p in pl:
-				if (p.cell.type == 'SB_IO') and ('BEL' in p.cell.attrs):
-					return BEL.from_json_attr(p.cell.attrs['BEL'])
-		return None
-
-	def add_lc(self, lc, fcid=None):
-		# Get Full Cell ID if not provided
-		if fcid is None:
-			grp = int(lc.attrs['SERDES_GRP'], 2)
-			fcid = FullCellId.from_json_attr(grp)
-
-		# Add to LCs
-		if self.lcs[fcid.lc] is not None:
-			raise RuntimeError(f'Duplicate LC for FullCellId {fcid}')
-
-		self.lcs[fcid.lc] = lc
-
-	def find_io_site(self):
-		for lc in self.lcs:
-			if lc is None:
-				continue
-			s = self._find_io_site_for_lc(lc)
-			if s is not None:
-				return s
-		return None
-
-	def analyze(self):
-		# Check and truncate LC array
-		l = len(self)
-		if not all([x is not None for x in self.lcs[0:l]]):
-			raise RuntimeError(f'Invalid group in block {self.group.gid}/{self.sid}/{self.typ}')
-
-		self.lcs = self.lcs[0:l]
-
-		# Identify IO site connection if there is one
-		self.io = self.find_io_site()
-
-		# Identify the control group
-		self.cg = ControlGroup.from_lc(self.lcs[0])
-
-	def assign_bel(self, base_bel, zofs=0):
-		for i, lc in enumerate(self.lcs):
-			lc.setAttr('BEL', 'X%d/Y%d/lc%d' % (base_bel.x, base_bel.y, base_bel.z + zofs + i))
-
-	def __len__(self):
-		return sum([x is not None for x in self.lcs])
-
-
-class PlacerSite:
-
-	def __init__(self, pos):
-		self.pos = pos
-		self.free = 8
-		self.blocks = []
-		self.cg = None
-
-	def valid_for_block(self, blk):
-		return (self.free >= len(blk)) and (
-			(self.cg is None) or
-			(blk.cg is None) or
-			(self.cg == blk.cg)
-		)
-
-	def add_block(self, blk):
-		# Assign the block into position
-		pos = BEL(self.pos.x, self.pos.y, 8-self.free)
-		blk.assign_bel(pos)
-
-		# Add to blocks here
-		self.blocks.append(blk)
-
-		# Update constrainsts
-		self.cg = blk.cg
-		self.free -= len(blk)
-
-		return pos
-
-
-class Placer:
-
-	PRIORITY = [
-		# Type	Place Target
-		(0x2,	lambda p, b: b.group.io),
-		(0xb,	lambda p, b: b.group.io),
-		(0xa,	lambda p, b: b.group.io),
-		(0x9,	lambda p, b: p.pos_of( b.group.blocks[(4|(b.sid & 1), 0xa)] ) ),
-		(0x1,	lambda p, b: b.group.io),
-		(0x0,	lambda p, b: p.pos_of( b.group.blocks[(b.sid, 0x1)]  ) ),
-		(0x8,	lambda p, b: p.pos_of( b.group.blocks[(4, 0xa)], b.group.blocks.get((5, 0xa)) ) ),
-	]
-
-	PLACE_PREF = [
-		# Xofs Yofs
-		( 0,  1),
-		(-1,  1),
-		( 1,  1),
-		(-1,  0),
-		( 1,  0),
-		( 0, -1),
-		(-1, -1),
-		( 1, -1),
-		( 0,  1),
-		( 0,  2),
-		( 0,  3),
-		( 0,  4),
-		(-1,  1),
-		( 1,  1),
-		(-1,  2),
-		( 1,  2),
-		(-1,  3),
-		( 1,  3),
-		(-1,  4),
-		( 1,  4),
-	]
-
-	def __init__(self, groups, top=False):
-		# Save groups to place
-		self.groups = groups
-
-		# Generate site grid
-		self.top = top
-		self.m_fwd  = {}
-		self.m_back = {}
-
-		for y in (range(26,31) if self.top else range(1,6)):
-			for x in range(1,25):
-				# Invalid, used by SPRAM
-				if x in [6,19]:
-					continue
-				self.m_fwd[BEL(x,y,0)] = PlacerSite(BEL(x,y, 0))
-
-	def _blocks_by_type(self, typ):
-		r = []
-		for grp in self.groups:
-			for blk in grp.blocks.values():
-				if blk.typ == typ:
-					r.append(blk)
-		return sorted(r, key=lambda b: (b.group.gid, b.sid))
-
-	def place(self):
-		# Scan by priority order
-		for typ, fn in self.PRIORITY:
-			# Collect all blocks per type and sorted by gid,sid
-			blocks = self._blocks_by_type(typ)
-
-			# Place each block
-			for blk in blocks:
-				# Get target location
-				tgt = fn(self, blk)
-
-				if type(tgt) == list:
-					x = int(round(sum([b.x for b in tgt]) / len(tgt)))
-					y = int(round(sum([b.y for b in tgt]) / len(tgt)))
-					tgt = BEL(x, y, 0)
-
-				# Scan placement preference and try to place
-				for xofs, yofs in self.PLACE_PREF:
-					# Flip for top
-					if self.top:
-						yofs = -yofs
-
-					p = BEL(tgt.x + xofs, tgt.y + yofs, 0)
-
-					if (p in self.m_fwd) and (self.m_fwd[p].valid_for_block(blk)):
-						self.place_block(blk, p)
-						break
-
-				else:
-					raise RuntimeError(f'Unable to place {blk}')
-
-		# Debug
-		if debug:
-			for g in self.groups:
-				print(f"Group {g.gid} for IO {g.io}")
-				for b in g.blocks.values():
-					print(f"\t{str(b):40s}: {len(b)} LCs placed @ {self.pos_of(b)}")
-				print()
-
-	def place_block(self, blk, pos):
-		self.m_back[blk] = self.m_fwd[pos].add_block(blk)
-
-	def pos_of(self, *blocks):
-		if len(blocks) > 1:
-			return [ self.m_back.get(b) for b in blocks if b is not None ]
-		else:
-			return self.m_back.get(blocks[0])
-
-
-
-# ----------------------------------------------------------------------------
-# Main
-# ----------------------------------------------------------------------------
-
-debug = True
-
-
-# Collect
-# -------
-
-groups = {}
-sync_lbuf_net_map = {}
-sync_lbuf_top = {}
-sync_lbuf_bot = {}
-
-for n,c in ctx.cells:
-	# Filter out dummy 'BEL' attributes
-	if 'BEL' in c.attrs:
-		if not c.attrs['BEL'].strip():
-			c.unsetAttr('BEL')
-
-	# Special processing
-	if 'SERDES_ATTR' in c.attrs:
-		attr = c.attrs['SERDES_ATTR']
-		c.unsetAttr('SERDES_ATTR')
-
-		# Local sync buffer
-		if attr.startswith('sync_lbuf'):
-			# Position
-			if attr.endswith('top'):
-				d = sync_lbuf_top
-			elif attr.endswith('bot'):
-				d = sync_lbuf_bot
-
-			# Sync source
-			src_net = c.ports['I0'].net
-			src_plb = BEL.from_json_attr(src_net.driver.cell.attrs['BEL'])[0:2]
-			dst_net = c.ports['O'].net
-
-			# Collect
-			sync_lbuf_net_map.setdefault(src_plb, []).append(src_net.name)
-			d[src_plb] = dst_net.name
-
-	# Does the cell need grouping ?
-	if 'SERDES_GRP' in c.attrs:
-		# Get group
-		grp = int(c.attrs['SERDES_GRP'], 2)
-		c.unsetAttr('SERDES_GRP')
-
-		# Skip invalid/dummy
-		if grp == 0xffffffff:
-			continue
-
-		# Add LC to our list
-		fcid = FullCellId.from_json_attr(grp)
-
-		if fcid.gid not in groups:
-			groups[fcid.gid] = SerDesGroup(fcid.gid)
-
-		groups[fcid.gid].add_lc(c, fcid=fcid)
-
-
-# Analyze and split into top/bottom
-# ---------------------------------
-
-groups_top = []
-groups_bot = []
-
-for g in groups.values():
-	# Analyze
-	g.analyze()
-
-	# Sort
-	if g.io.y == 0:
-		groups_bot.append(g)
-	else:
-		groups_top.append(g)
-
-
-# Process local buffers
-# ---------------------
-
-def lbuf_build_map(src_net_map, dst_net_map):
-	rv = {}
-	for k, v in dst_net_map.items():
-		for n in src_net_map[k]:
-			rv[n] = v
-	return rv
-
-def lbuf_reconnect(net_map, cells):
-	# Scan all cells
-	for lc in cells:
-		# Scan all ports
-		for pn in ['I0', 'I1', 'I2', 'I3', 'CEN']:
-			n = lc.ports[pn].net
-			if (n is not None) and (n.name in net_map):
-				# Reconnect
-				ctx.disconnectPort(lc.name, pn)
-				ctx.connectPort(net_map[n.name], lc.name, pn)
-
-
-sync_lbuf_top = lbuf_build_map(sync_lbuf_net_map, sync_lbuf_top)
-sync_lbuf_bot = lbuf_build_map(sync_lbuf_net_map, sync_lbuf_bot)
-
-for g in groups_top:
-	for blk in g.blocks.values():
-		lbuf_reconnect(sync_lbuf_top, blk.lcs)
-
-for g in groups_bot:
-	for blk in g.blocks.values():
-		lbuf_reconnect(sync_lbuf_bot, blk.lcs)
-
-
-# Execute placer
-# --------------
-
-if groups_top:
-	p_top = Placer(groups_top, top=True)
-	p_top.place()
-
-if groups_bot:
-	p_bot = Placer(groups_bot, top=False)
-	p_bot.place()

+ 1 - 0
cores/no2ice40

@@ -0,0 +1 @@
+Subproject commit e47c023fbc6da9f1ffc43e8c07b1ede8961a3b3d