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