gateware: Wrap capture/counter units and allow use of SB_MAC16

We have a bunch of Multiply Add units that are un-used, we can
make use of the "accumulate" part to implement the few wide
counters we have to win some LCs.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
diff --git a/gateware/common/rtl/capcnt.v b/gateware/common/rtl/capcnt.v
new file mode 100644
index 0000000..615d37b
--- /dev/null
+++ b/gateware/common/rtl/capcnt.v
@@ -0,0 +1,71 @@
+/*
+ * capcnt.v
+ *
+ * Simple capture/counter blocks
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: CERN-OHL-P-2.0
+ */
+
+`define WITH_SB_MAC16
+
+module capcnt #(
+	parameter integer W = 16,
+)(
+	output wire [W-1:0] cnt_cur,
+	output wire [W-1:0] cnt_cap,
+	input  wire         inc,
+	input  wire         cap,
+	input  wire         clk,
+	input  wire         rst
+);
+
+`ifdef WITH_SB_MAC16
+	generate
+
+		if (W == 16)
+			capcnt16_sb_mac16 sub_I (
+				.cnt_cur (cnt_cur),
+				.cnt_cap (cnt_cap),
+				.inc     (inc),
+				.cap     (cap),
+				.clk     (clk),
+				.rst     (rst)
+			);
+
+		else if (W == 32)
+			capcnt32_sb_mac16 sub_I (
+				.cnt_cur (cnt_cur),
+				.cnt_cap (cnt_cap),
+				.inc     (inc),
+				.cap     (cap),
+				.clk     (clk),
+				.rst     (rst)
+			);
+
+	endgenerate
+`else
+
+	reg [W-1:0] cnt_cur_i;
+	reg [W-1:0] cnt_cap_i;
+
+	always @(posedge clk)
+		if (rst)
+			cnt_cur_i <= 0;
+		else if (inc)
+			cnt_cur_i <= cnt_cur_i + 1;
+
+	always @(posedge clk)
+		if (rst)
+			cnt_cap_i <= 0;
+		else if (cap)
+			cnt_cap_i <= cnt_cur_i;
+
+	assign cnt_cur = cnt_cur_i;
+	assign cnt_cap = cnt_cap_i;
+
+`endif
+
+endmodule // capcnt
diff --git a/gateware/common/rtl/capcnt_sb_mac16.v b/gateware/common/rtl/capcnt_sb_mac16.v
new file mode 100644
index 0000000..a82e9d0
--- /dev/null
+++ b/gateware/common/rtl/capcnt_sb_mac16.v
@@ -0,0 +1,211 @@
+/*
+ * capcnt_sb_mac16.v
+ *
+ * Helper to use the SB_MAC16 as simple capture/counter blocks
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
+ * SPDX-License-Identifier: CERN-OHL-P-2.0
+ */
+
+`default_nettype none
+
+module capcnt16_sb_mac16 (
+	output wire [15:0] cnt_cur,
+	output wire [15:0] cnt_cap,
+	input  wire        inc,
+	input  wire        cap,
+	input  wire        clk,
+	input  wire        rst
+);
+
+	// TOP = Count
+	// Top unit configured as 16 bit accumulator with fixed carry=1 input.
+	// Most of input/registers are HOLD=1 and forced to reset. The output
+	// register HOLD input is used for conditional increment.
+	//
+	// BOT = Capture
+	// Bottom unit is configured for the output register to do fixed loading
+	// from D input which is wired to the current counter value. HOLD input
+	// used for capture.
+
+	SB_MAC16 #(
+		.NEG_TRIGGER             (1'b0),
+		.C_REG                   (1'b1),
+		.A_REG                   (1'b1),
+		.B_REG                   (1'b1),
+		.D_REG                   (1'b0),
+		.TOP_8x8_MULT_REG        (1'b1),
+		.BOT_8x8_MULT_REG        (1'b1),
+		.PIPELINE_16x16_MULT_REG1(1'b1),
+		.PIPELINE_16x16_MULT_REG2(1'b1),
+		.TOPOUTPUT_SELECT        (2'b01),
+		.TOPADDSUB_LOWERINPUT    (2'b00),
+		.TOPADDSUB_UPPERINPUT    (1'b0),
+		.TOPADDSUB_CARRYSELECT   (2'b01),
+		.BOTOUTPUT_SELECT        (2'b01),
+		.BOTADDSUB_LOWERINPUT    (2'b00),
+		.BOTADDSUB_UPPERINPUT    (1'b0),
+		.BOTADDSUB_CARRYSELECT   (2'b00),
+		.MODE_8x8                (1'b1),
+		.A_SIGNED                (1'b0),
+		.B_SIGNED                (1'b0)
+	) mac_I (
+		.CLK        (clk),
+		.CE         (1'b1),
+		.C          (16'h0000),
+		.A          (16'h0000),
+		.B          (16'h0000),
+		.D          (cnt_cur),
+		.AHOLD      (1'b1),
+		.BHOLD      (1'b1),
+		.CHOLD      (1'b1),
+		.DHOLD      (1'b1),
+		.IRSTTOP    (1'b1),
+		.IRSTBOT    (1'b1),
+		.ORSTTOP    (rst),
+		.ORSTBOT    (rst),
+		.OLOADTOP   (1'b0),
+		.OLOADBOT   (1'b1),
+		.ADDSUBTOP  (1'b0),
+		.ADDSUBBOT  (1'b0),
+		.OHOLDTOP   (~inc),
+		.OHOLDBOT   (~cap),
+		.CI         (),
+		.ACCUMCI    (),
+		.SIGNEXTIN  (),
+		.O          ({cnt_cur, cnt_cap}),
+		.CO         (),
+		.ACCUMCO    (),
+		.SIGNEXTOUT ()
+	);
+
+endmodule // capcnt16_sb_mac16
+
+
+module capcnt32_sb_mac16 (
+	output wire [31:0] cnt_cur,
+	output wire [31:0] cnt_cap,
+	input  wire        inc,
+	input  wire        cap,
+	input  wire        clk,
+	input  wire        rst
+);
+
+	// Counting
+	// --------
+	// Hi/Lo configured as 32 bit accumulator adding a constant carry=1
+	// at every cycle. Most register are held in reset and with HOLD=1.
+	// HOLD input on Output reg used to implement conditional 'increment'.
+
+	SB_MAC16 #(
+		.NEG_TRIGGER             (1'b0),
+		.C_REG                   (1'b1),
+		.A_REG                   (1'b1),
+		.B_REG                   (1'b1),
+		.D_REG                   (1'b1),
+		.TOP_8x8_MULT_REG        (1'b1),
+		.BOT_8x8_MULT_REG        (1'b1),
+		.PIPELINE_16x16_MULT_REG1(1'b1),
+		.PIPELINE_16x16_MULT_REG2(1'b1),
+		.TOPOUTPUT_SELECT        (2'b01),
+		.TOPADDSUB_LOWERINPUT    (2'b00),
+		.TOPADDSUB_UPPERINPUT    (1'b0),
+		.TOPADDSUB_CARRYSELECT   (2'b10),
+		.BOTOUTPUT_SELECT        (2'b01),
+		.BOTADDSUB_LOWERINPUT    (2'b00),
+		.BOTADDSUB_UPPERINPUT    (1'b0),
+		.BOTADDSUB_CARRYSELECT   (2'b01),
+		.MODE_8x8                (1'b1),
+		.A_SIGNED                (1'b0),
+		.B_SIGNED                (1'b0)
+	) cnt_mac_I (
+		.CLK        (clk),
+		.CE         (1'b1),
+		.C          (16'h0000),
+		.A          (16'h0000),
+		.B          (16'h0000),
+		.D          (16'h0000),
+		.AHOLD      (1'b1),
+		.BHOLD      (1'b1),
+		.CHOLD      (1'b1),
+		.DHOLD      (1'b1),
+		.IRSTTOP    (1'b1),
+		.IRSTBOT    (1'b1),
+		.ORSTTOP    (rst),
+		.ORSTBOT    (rst),
+		.OLOADTOP   (1'b0),
+		.OLOADBOT   (1'b0),
+		.ADDSUBTOP  (1'b0),
+		.ADDSUBBOT  (1'b0),
+		.OHOLDTOP   (~inc),
+		.OHOLDBOT   (~inc),
+		.CI         (),
+		.ACCUMCI    (),
+		.SIGNEXTIN  (),
+		.O          (cnt_cur),
+		.CO         (),
+		.ACCUMCO    (),
+		.SIGNEXTOUT ()
+	);
+
+
+	// Capture
+	// -------
+	// Output register is used to capture the value.
+	// It's loaded from {C,D} and using HOLD on the output
+	// register to implement capture trigger.
+
+	SB_MAC16 #(
+		.NEG_TRIGGER             (1'b0),
+		.C_REG                   (1'b0),
+		.A_REG                   (1'b1),
+		.B_REG                   (1'b1),
+		.D_REG                   (1'b0),
+		.TOP_8x8_MULT_REG        (1'b1),
+		.BOT_8x8_MULT_REG        (1'b1),
+		.PIPELINE_16x16_MULT_REG1(1'b1),
+		.PIPELINE_16x16_MULT_REG2(1'b1),
+		.TOPOUTPUT_SELECT        (2'b01),
+		.TOPADDSUB_LOWERINPUT    (2'b00),
+		.TOPADDSUB_UPPERINPUT    (1'b0),
+		.TOPADDSUB_CARRYSELECT   (2'b00),
+		.BOTOUTPUT_SELECT        (2'b01),
+		.BOTADDSUB_LOWERINPUT    (2'b00),
+		.BOTADDSUB_UPPERINPUT    (1'b0),
+		.BOTADDSUB_CARRYSELECT   (2'b00),
+		.MODE_8x8                (1'b1),
+		.A_SIGNED                (1'b0),
+		.B_SIGNED                (1'b0)
+	) cap_mac_I (
+		.CLK        (clk),
+		.CE         (1'b1),
+		.C          (cnt_cur[31:16]),
+		.A          (16'h0000),
+		.B          (16'h0000),
+		.D          (cnt_cur[15:0]),
+		.AHOLD      (1'b1),
+		.BHOLD      (1'b1),
+		.CHOLD      (1'b1),
+		.DHOLD      (1'b1),
+		.IRSTTOP    (1'b1),
+		.IRSTBOT    (1'b1),
+		.ORSTTOP    (rst),
+		.ORSTBOT    (rst),
+		.OLOADTOP   (1'b1),
+		.OLOADBOT   (1'b1),
+		.ADDSUBTOP  (1'b0),
+		.ADDSUBBOT  (1'b0),
+		.OHOLDTOP   (~cap),
+		.OHOLDBOT   (~cap),
+		.CI         (),
+		.ACCUMCI    (),
+		.SIGNEXTIN  (),
+		.O          (cnt_cap),
+		.CO         (),
+		.ACCUMCO    (),
+		.SIGNEXTOUT ()
+	);
+
+endmodule // capcnt32_sb_mac16
diff --git a/gateware/e1-tracer/Makefile b/gateware/e1-tracer/Makefile
index b92a258..bd51a79 100644
--- a/gateware/e1-tracer/Makefile
+++ b/gateware/e1-tracer/Makefile
@@ -7,6 +7,8 @@
 	sysmgr.v \
 )
 PROJ_RTL_SRCS += $(addprefix ../common/rtl/, \
+	capcnt.v \
+	capcnt_sb_mac16.v \
 	dfu_helper.v \
 	picorv32.v \
 	picorv32_ice40_regs.v \
diff --git a/gateware/e1-tracer/rtl/misc.v b/gateware/e1-tracer/rtl/misc.v
index c64ad8b..e39a787 100644
--- a/gateware/e1-tracer/rtl/misc.v
+++ b/gateware/e1-tracer/rtl/misc.v
@@ -45,9 +45,8 @@
 	reg  bus_we_boot;
 
 	// Counters
-	reg  [15:0] cnt_e1_rx[0:1];
-	reg  [15:0] cap_e1_rx[0:1];
-	reg  [31:0] cnt_time;
+	wire [15:0] cap_e1_rx[0:1];
+	wire [31:0] cnt_time;
 
 	// Boot
 	reg   [1:0] boot_sel;
@@ -87,26 +86,28 @@
 	// --------
 
 	// E1 ticks
-	for (i=0; i<2; i=i+1) begin
+	capcnt #(
+		.W(16)
+	) e1_cnt_I[1:0] (
+		.cnt_cur (),
+		.cnt_cap ({cap_e1_rx[1],  cap_e1_rx[0] }),
+		.inc     ({tick_e1_rx[1], tick_e1_rx[0]}),
+		.cap     (tick_usb_sof),
+		.clk     (clk),
+		.rst     (rst)
+	);
 
-		always @(posedge clk or posedge rst)
-			if (rst)
-				cnt_e1_rx[i] <= 16'h0000;
-			else if (tick_e1_rx[i])
-				cnt_e1_rx[i] <= cnt_e1_rx[i] + 1;
-
-		always @(posedge clk)
-			if (tick_usb_sof)
-				cap_e1_rx[i] <= cnt_e1_rx[i];
-
-	end
-
-	// Time counter
-	always @(posedge clk)
-		if (rst)
-			cnt_time <= 32'h00000000;
-		else
-			cnt_time <= cnt_time + 1;
+	// Time
+	capcnt #(
+		.W(32)
+	) time_cnt_I (
+		.cnt_cur (cnt_time),
+		.cnt_cap (),
+		.inc     (1'b1),
+		.cap     (1'b0),
+		.clk     (clk),
+		.rst     (rst)
+	);
 
 
 	// DFU / Reboot
diff --git a/gateware/icE1usb-proto/Makefile b/gateware/icE1usb-proto/Makefile
index 59db8ff..bd54a0f 100644
--- a/gateware/icE1usb-proto/Makefile
+++ b/gateware/icE1usb-proto/Makefile
@@ -7,6 +7,8 @@
 	sysmgr.v \
 )
 PROJ_RTL_SRCS += $(addprefix ../common/rtl/, \
+	capcnt.v \
+	capcnt_sb_mac16.v \
 	dfu_helper.v \
 	picorv32.v \
 	picorv32_ice40_regs.v \
diff --git a/gateware/icE1usb-proto/rtl/misc.v b/gateware/icE1usb-proto/rtl/misc.v
index 63c315d..934ba05 100644
--- a/gateware/icE1usb-proto/rtl/misc.v
+++ b/gateware/icE1usb-proto/rtl/misc.v
@@ -54,11 +54,9 @@
 	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;
+	wire [15:0] cap_e1_rx;
+	wire [15:0] cap_e1_tx;
+	wire [31:0] cnt_time;
 
 	// PDM
 	reg  [12:0] pdm_clk[0:1];
@@ -117,30 +115,28 @@
 	// --------
 
 	// 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;
+	capcnt #(
+		.W(16)
+	) e1_cnt_I[1:0] (
+		.cnt_cur (),
+		.cnt_cap ({cap_e1_tx,  cap_e1_rx }),
+		.inc     ({tick_e1_tx, tick_e1_rx}),
+		.cap     (tick_usb_sof),
+		.clk     (clk),
+		.rst     (rst)
+	);
 
-	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;
+	// Time
+	capcnt #(
+		.W(32)
+	) time_cnt_I (
+		.cnt_cur (cnt_time),
+		.cnt_cap (),
+		.inc     (1'b1),
+		.cap     (1'b0),
+		.clk     (clk),
+		.rst     (rst)
+	);
 
 
 	// PDM outputs
diff --git a/gateware/icE1usb/Makefile b/gateware/icE1usb/Makefile
index ca1698a..759896f 100644
--- a/gateware/icE1usb/Makefile
+++ b/gateware/icE1usb/Makefile
@@ -11,6 +11,8 @@
 	sysmgr.v \
 )
 PROJ_RTL_SRCS += $(addprefix ../common/rtl/, \
+	capcnt.v \
+	capcnt_sb_mac16.v \
 	dfu_helper.v \
 	picorv32.v \
 	picorv32_ice40_regs.v \
diff --git a/gateware/icE1usb/rtl/misc.v b/gateware/icE1usb/rtl/misc.v
index a1d1a9c..634ed5d 100644
--- a/gateware/icE1usb/rtl/misc.v
+++ b/gateware/icE1usb/rtl/misc.v
@@ -81,12 +81,10 @@
 	wire gps_pps_r;
 
 	// Counters
-	reg  [15:0] cnt_e1_rx[0:1];
-	reg  [15:0] cap_e1_rx[0:1];
-	reg  [15:0] cnt_e1_tx[0:1];
-	reg  [15:0] cap_e1_tx[0:1];
-	reg  [31:0] cap_gps;
-	reg  [31:0] cnt_time;
+	wire [15:0] cap_e1_rx[0:1];
+	wire [15:0] cap_e1_tx[0:1];
+	wire [31:0] cap_gps;
+	wire [31:0] cnt_time;
 
 	// PDM
 	reg  [12:0] pdm_clk[0:1];
@@ -227,41 +225,28 @@
 	// --------
 
 	// E1 ticks
-	for (i=0; i<2; i=i+1) begin
+	capcnt #(
+		.W(16)
+	) e1_cnt_I[3:0] (
+		.cnt_cur (),
+		.cnt_cap ({cap_e1_tx[1],  cap_e1_rx[1],  cap_e1_tx[0],  cap_e1_rx[0] }),
+		.inc     ({tick_e1_tx[1], tick_e1_rx[1], tick_e1_tx[0], tick_e1_rx[0]}),
+		.cap     (tick_usb_sof),
+		.clk     (clk),
+		.rst     (rst)
+	);
 
-		always @(posedge clk or posedge rst)
-			if (rst)
-				cnt_e1_rx[i] <= 16'h0000;
-			else if (tick_e1_rx[i])
-				cnt_e1_rx[i] <= cnt_e1_rx[i] + 1;
-
-		always @(posedge clk or posedge rst)
-			if (rst)
-				cnt_e1_tx[i] <= 16'h0000;
-			else if (tick_e1_tx[i])
-				cnt_e1_tx[i] <= cnt_e1_tx[i] + 1;
-
-		always @(posedge clk)
-			if (tick_usb_sof) begin
-				cap_e1_rx[i] <= cnt_e1_rx[i];
-				cap_e1_tx[i] <= cnt_e1_tx[i];
-			end
-
-	end
-
-	// GPS
-	always @(posedge clk or posedge rst)
-		if (rst)
-			cap_gps <= 32'h00000000;
-		else if (gps_pps_r)
-			cap_gps <= cnt_time;
-
-	// Time counter
-	always @(posedge clk)
-		if (rst)
-			cnt_time <= 32'h00000000;
-		else
-			cnt_time <= cnt_time + 1;
+	// Time / GPS
+	capcnt #(
+		.W(32)
+	) time_cnt_I (
+		.cnt_cur (cnt_time),
+		.cnt_cap (cap_gps),
+		.inc     (1'b1),
+		.cap     (gps_pps_r),
+		.clk     (clk),
+		.rst     (rst)
+	);
 
 
 	// PDM outputs