Browse Source

cores/ice40: Add support for SERDES sync signal local buffer

This helps distribute the critical sync signal to both top & bottom
IO banks at very high frequency. Tested working with 200 MHz hyperram

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 4 years ago
parent
commit
caf64670e2

+ 17 - 16
cores/ice40/rtl/ice40_serdes_dff.v

@@ -38,6 +38,7 @@ module ice40_serdes_dff #(
 	parameter integer ENA = 0,
 	parameter integer RST = 0,
 	parameter integer SERDES_GRP = -1,
+	parameter SERDES_ATTR = "",
 	parameter BEL = ""
 )(
 	input  wire d,
@@ -50,8 +51,8 @@ module ice40_serdes_dff #(
 
 	generate
 		if (TYPE == 0)			// Simple
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFF dff_I (
 				.D(d),
 				.Q(q),
@@ -59,8 +60,8 @@ module ice40_serdes_dff #(
 			);
 
 		else if (TYPE == 1)		// NEG
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFFN dff_I (
 				.D(d),
 				.Q(q),
@@ -68,8 +69,8 @@ module ice40_serdes_dff #(
 			);
 
 		else if (TYPE == 2)		//     ENA
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFFE dff_I (
 				.D(d),
 				.Q(q),
@@ -78,8 +79,8 @@ module ice40_serdes_dff #(
 			);
 
 		else if (TYPE == 3)		// NEG ENA
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFFNE dff_I (
 				.D(d),
 				.Q(q),
@@ -88,8 +89,8 @@ module ice40_serdes_dff #(
 			);
 
 		else if (TYPE == 4)		//         RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFFR dff_I (
 				.D(d),
 				.Q(q),
@@ -98,8 +99,8 @@ module ice40_serdes_dff #(
 			);
 
 		else if (TYPE == 5)		// NEG     RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFFNR dff_I (
 				.D(d),
 				.Q(q),
@@ -108,8 +109,8 @@ module ice40_serdes_dff #(
 			);
 
 		else if (TYPE == 6)		//     ENA RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFFER dff_I (
 				.D(d),
 				.Q(q),
@@ -119,8 +120,8 @@ module ice40_serdes_dff #(
 			);
 
 		else if (TYPE == 7)		// NEG ENA RST
-			(* BEL=BEL, SERDES_GRP=SERDES_GRP *)
-			(* dont_touch *)
+			(* BEL=BEL, SERDES_GRP=SERDES_GRP, SERDES_ATTR=SERDES_ATTR *)
+			(* dont_touch, keep *)
 			SB_DFFNER dff_I (
 				.D(d),
 				.Q(q),

+ 68 - 21
cores/ice40/rtl/ice40_serdes_sync.v

@@ -37,13 +37,18 @@ module ice40_serdes_sync #(
 	parameter integer PHASE = 0,
 	parameter integer NEG_EDGE = 0,
 	parameter integer GLOBAL_BUF = 0,
-	parameter BEL_BASE = "X12/Y15"
+	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;
@@ -51,14 +56,14 @@ module ice40_serdes_sync #(
 	wire [1:0] cnt_next;
 	wire [1:0] cnt_val;
 
-	wire       sync_next;
-	wire       sync_i;
+	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, "/lc0"})
+		.BEL({BEL_BASE, "/lc7"})
 	) ff_samp0_I (
 		.d(clk_slow),
 		.q(clk_samp[0]),
@@ -69,7 +74,7 @@ module ice40_serdes_sync #(
 	ice40_serdes_dff #(
 		.NEG(NEG_EDGE),
 		.RST(1),
-		.BEL({BEL_BASE, "/lc1"})
+		.BEL({BEL_BASE, "/lc6"})
 	) ff_samp1_I (
 		.d(clk_samp[0]),
 		.q(clk_samp[1]),
@@ -84,7 +89,7 @@ module ice40_serdes_sync #(
 	ice40_serdes_dff #(
 		.NEG(NEG_EDGE),
 		.RST(1),
-		.BEL({BEL_BASE, "/lc2"})
+		.BEL({BEL_BASE, "/lc5"})
 	) ff_edge0_I (
 		.d(edge_det[0]),
 		.q(edge_found[0]),
@@ -95,7 +100,7 @@ module ice40_serdes_sync #(
 	ice40_serdes_dff #(
 		.NEG(NEG_EDGE),
 		.RST(1),
-		.BEL({BEL_BASE, "/lc3"})
+		.BEL({BEL_BASE, "/lc4"})
 	) ff_edge1_I (
 		.d(edge_det[1]),
 		.q(edge_found[1]),
@@ -110,7 +115,18 @@ module ice40_serdes_sync #(
 	ice40_serdes_dff #(
 		.NEG(NEG_EDGE),
 		.RST(1),
-		.BEL({BEL_BASE, "/lc4"})
+		.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]),
@@ -118,27 +134,35 @@ module ice40_serdes_sync #(
 		.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, "/lc5"})
-	) ff_cnt1_I (
-		.d(cnt_next[1]),
-		.q(cnt_val[1]),
+		.BEL({BEL_BASE, "/lc3"})
+	) ff_sync1_I (
+		.d(sync_next[1]),
+		.q(sync_i[1]),
 		.c(clk_fast),
 		.r(rst)
 	);
 
-	// Final comparator
-	assign sync_next = edge_found[1] & (cnt_val == PHASE);
-
 	ice40_serdes_dff #(
 		.NEG(NEG_EDGE),
 		.RST(1),
-		.BEL({BEL_BASE, "/lc6"})
-	) ff_sync_I (
-		.d(sync_next),
-		.q(sync_i),
+		.BEL({BEL_BASE, "/lc0"})
+	) ff_sync0_I (
+		.d(sync_next[0]),
+		.q(sync_i[0]),
 		.c(clk_fast),
 		.r(rst)
 	);
@@ -146,12 +170,35 @@ module ice40_serdes_sync #(
 	// Buffer ?
 	generate
 		if (GLOBAL_BUF)
+			(* BEL=BEL_GB *)
 			SB_GB gbuf_sync_I (
-				.USER_SIGNAL_TO_GLOBAL_BUFFER(sync_i),
+				.USER_SIGNAL_TO_GLOBAL_BUFFER(sync_i[0]),
 				.GLOBAL_BUFFER_OUTPUT(sync)
 			);
 		else
-			assign sync = sync_i;
+			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

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

@@ -346,6 +346,9 @@ debug = True
 # -------
 
 groups = {}
+sync_lbuf_net_map = {}
+sync_lbuf_top = {}
+sync_lbuf_bot = {}
 
 for n,c in ctx.cells:
 	# Filter out dummy 'BEL' attributes
@@ -353,6 +356,28 @@ for n,c in ctx.cells:
 		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
@@ -389,6 +414,40 @@ for g in groups.values():
 		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
 # --------------