Add collapsering_macro. Hardens okay.

Doesn't "meet" timing, but there really isn't any timing to meet here
except in the FF clock dividers which are suspicious and best effort
anyways.
diff --git a/ip/randsack/rtl/collapsering.v b/ip/randsack/rtl/collapsering.v
new file mode 100644
index 0000000..2de645f
--- /dev/null
+++ b/ip/randsack/rtl/collapsering.v
@@ -0,0 +1,718 @@
+// SPDX-FileCopyrightText: 2021 Harrison Pham
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// SPDX-License-Identifier: Apache-2.0
+
+module collapsering (
+  output wire CLKBUFOUT,
+  input wire START,
+  input wire [27:0] TRIMA,
+  input wire [27:0] TRIMB,
+  input wire [2:0] CLKMUX
+);
+
+wire net1  ;
+wire net2  ;
+wire net3  ;
+wire net4  ;
+wire net5  ;
+wire net6  ;
+wire net7  ;
+wire net8  ;
+wire net9  ;
+wire net20  ;
+wire net21  ;
+wire net22  ;
+wire net23  ;
+wire net24  ;
+wire clk_ff0  ;
+wire net25  ;
+wire clk_ff1  ;
+wire net26  ;
+wire net40  ;
+wire clk_ff2  ;
+wire net27  ;
+wire net41  ;
+wire clk_ff3  ;
+wire net28  ;
+wire net42  ;
+wire net29  ;
+wire net10  ;
+wire net11  ;
+wire outa  ;
+wire outb  ;
+wire net12  ;
+wire net13  ;
+wire net14  ;
+wire net15  ;
+wire net16  ;
+wire net17  ;
+wire net30  ;
+wire net18  ;
+wire net31  ;
+wire net19  ;
+wire net32  ;
+wire net33  ;
+wire net34  ;
+wire net35  ;
+wire net36  ;
+wire net37  ;
+wire net38  ;
+wire net39  ;
+
+ sky130_fd_sc_hd__nand2_4
+#(
+)
+x21 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( outb ),
+ .B( net25 ),
+ .Y( net1 )
+);
+
+
+sky130_fd_sc_hd__nand2_4
+#(
+)
+x47 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( net25 ),
+ .B( outa ),
+ .Y( net16 )
+);
+
+
+sky130_fd_sc_hd__clkbuf_4
+#(
+)
+x35 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( net38 ),
+ .X( CLKBUFOUT )
+);
+
+
+triminv
+x1 (
+ .OUT( net2 ),
+ .IN( net1 ),
+ .TRIM0( TRIMA[0] ),
+ .TRIM1( TRIMA[1] )
+);
+
+
+triminv
+x2 (
+ .OUT( net3 ),
+ .IN( net2 ),
+ .TRIM0( TRIMA[2] ),
+ .TRIM1( TRIMA[3] )
+);
+
+
+triminv
+x3 (
+ .OUT( net4 ),
+ .IN( net3 ),
+ .TRIM0( TRIMA[4] ),
+ .TRIM1( TRIMA[5] )
+);
+
+
+triminv
+x4 (
+ .OUT( net5 ),
+ .IN( net4 ),
+ .TRIM0( TRIMA[6] ),
+ .TRIM1( TRIMA[7] )
+);
+
+
+triminv
+x5 (
+ .OUT( net6 ),
+ .IN( net5 ),
+ .TRIM0( TRIMA[8] ),
+ .TRIM1( TRIMA[9] )
+);
+
+
+triminv
+x6 (
+ .OUT( net7 ),
+ .IN( net6 ),
+ .TRIM0( TRIMA[10] ),
+ .TRIM1( TRIMA[11] )
+);
+
+
+triminv
+x7 (
+ .OUT( net8 ),
+ .IN( net7 ),
+ .TRIM0( TRIMA[12] ),
+ .TRIM1( TRIMA[13] )
+);
+
+
+triminv
+x8 (
+ .OUT( net24 ),
+ .IN( net8 ),
+ .TRIM0( TRIMA[14] ),
+ .TRIM1( TRIMA[15] )
+);
+
+
+triminv
+x9 (
+ .OUT( net17 ),
+ .IN( net24 ),
+ .TRIM0( TRIMA[16] ),
+ .TRIM1( TRIMA[17] )
+);
+
+
+triminv
+x10 (
+ .OUT( net18 ),
+ .IN( net17 ),
+ .TRIM0( TRIMA[18] ),
+ .TRIM1( TRIMA[19] )
+);
+
+
+triminv
+x11 (
+ .OUT( net19 ),
+ .IN( net18 ),
+ .TRIM0( TRIMA[20] ),
+ .TRIM1( TRIMA[21] )
+);
+
+
+triminv
+x12 (
+ .OUT( net39 ),
+ .IN( net19 ),
+ .TRIM0( TRIMA[22] ),
+ .TRIM1( TRIMA[23] )
+);
+
+
+triminv
+x13 (
+ .OUT( net9 ),
+ .IN( net16 ),
+ .TRIM0( TRIMB[0] ),
+ .TRIM1( TRIMB[1] )
+);
+
+
+triminv
+x14 (
+ .OUT( net10 ),
+ .IN( net9 ),
+ .TRIM0( TRIMB[2] ),
+ .TRIM1( TRIMB[3] )
+);
+
+
+triminv
+x15 (
+ .OUT( net11 ),
+ .IN( net10 ),
+ .TRIM0( TRIMB[4] ),
+ .TRIM1( TRIMB[5] )
+);
+
+
+triminv
+x16 (
+ .OUT( net12 ),
+ .IN( net11 ),
+ .TRIM0( TRIMB[6] ),
+ .TRIM1( TRIMB[7] )
+);
+
+
+triminv
+x17 (
+ .OUT( net13 ),
+ .IN( net12 ),
+ .TRIM0( TRIMB[8] ),
+ .TRIM1( TRIMB[9] )
+);
+
+
+triminv
+x18 (
+ .OUT( net14 ),
+ .IN( net13 ),
+ .TRIM0( TRIMB[10] ),
+ .TRIM1( TRIMB[11] )
+);
+
+
+triminv
+x19 (
+ .OUT( net15 ),
+ .IN( net14 ),
+ .TRIM0( TRIMB[12] ),
+ .TRIM1( TRIMB[13] )
+);
+
+
+triminv
+x20 (
+ .OUT( net23 ),
+ .IN( net15 ),
+ .TRIM0( TRIMB[14] ),
+ .TRIM1( TRIMB[15] )
+);
+
+
+triminv
+x22 (
+ .OUT( net20 ),
+ .IN( net23 ),
+ .TRIM0( TRIMB[16] ),
+ .TRIM1( TRIMB[17] )
+);
+
+
+triminv
+x23 (
+ .OUT( net21 ),
+ .IN( net20 ),
+ .TRIM0( TRIMB[18] ),
+ .TRIM1( TRIMB[19] )
+);
+
+
+triminv
+x24 (
+ .OUT( net22 ),
+ .IN( net21 ),
+ .TRIM0( TRIMB[20] ),
+ .TRIM1( TRIMB[21] )
+);
+
+
+triminv
+x25 (
+ .OUT( net41 ),
+ .IN( net22 ),
+ .TRIM0( TRIMB[22] ),
+ .TRIM1( TRIMB[23] )
+);
+
+
+sky130_fd_sc_hd__dfxbp_2
+#(
+)
+x27 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .CLK( clk_ff0 ),
+ .D( net27 ),
+ .Q( clk_ff1 ),
+ .Q_N( net27 )
+);
+
+
+sky130_fd_sc_hd__dfxbp_2
+#(
+)
+x28 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .CLK( clk_ff0 ),
+ .D( net28 ),
+ .Q( net26 ),
+ .Q_N( net28 )
+);
+
+
+sky130_fd_sc_hd__dfxbp_2
+#(
+)
+x29 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .CLK( net26 ),
+ .D( net29 ),
+ .Q( clk_ff2 ),
+ .Q_N( net29 )
+);
+
+
+sky130_fd_sc_hd__clkbuf_8
+#(
+)
+x26 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( net30 ),
+ .X( clk_ff0 )
+);
+
+
+sky130_fd_sc_hd__mux4_2
+#(
+)
+x30 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A0( clk_ff0 ),
+ .A1( clk_ff1 ),
+ .A2( clk_ff2 ),
+ .A3( clk_ff3 ),
+ .S0( CLKMUX[0] ),
+ .S1( CLKMUX[1] ),
+ .X( net38 )
+);
+
+
+sky130_fd_sc_hd__dfxbp_2
+#(
+)
+x31 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .CLK( clk_ff0 ),
+ .D( net32 ),
+ .Q( net31 ),
+ .Q_N( net32 )
+);
+
+
+sky130_fd_sc_hd__dfxbp_2
+#(
+)
+x32 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .CLK( net31 ),
+ .D( net33 ),
+ .Q( net34 ),
+ .Q_N( net33 )
+);
+
+
+sky130_fd_sc_hd__dfxbp_2
+#(
+)
+x33 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .CLK( net34 ),
+ .D( net35 ),
+ .Q( clk_ff3 ),
+ .Q_N( net35 )
+);
+
+
+sky130_fd_sc_hd__mux2_2
+#(
+)
+x34 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A0( net36 ),
+ .A1( net37 ),
+ .S( CLKMUX[2] ),
+ .X( net30 )
+);
+
+
+sky130_fd_sc_hd__clkbuf_2
+#(
+)
+x36 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( outb ),
+ .X( net36 )
+);
+
+
+sky130_fd_sc_hd__clkbuf_2
+#(
+)
+x37 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( outa ),
+ .X( net37 )
+);
+
+
+sky130_fd_sc_hd__clkbuf_2
+#(
+)
+x38 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( START ),
+ .X( net25 )
+);
+
+
+triminv
+x39 (
+ .OUT( net40 ),
+ .IN( net39 ),
+ .TRIM0( TRIMA[24] ),
+ .TRIM1( TRIMA[25] )
+);
+
+
+triminv
+x40 (
+ .OUT( outa ),
+ .IN( net40 ),
+ .TRIM0( TRIMA[26] ),
+ .TRIM1( TRIMA[27] )
+);
+
+
+triminv
+x41 (
+ .OUT( net42 ),
+ .IN( net41 ),
+ .TRIM0( TRIMB[24] ),
+ .TRIM1( TRIMB[25] )
+);
+
+
+triminv
+x42 (
+ .OUT( outb ),
+ .IN( net42 ),
+ .TRIM0( TRIMB[26] ),
+ .TRIM1( TRIMB[27] )
+);
+
+endmodule
+
+// expanding   symbol:  /home/harrison/workspace/randsack/ip/randsack/sch/triminv.sym # of pins=4
+// sym_path: /home/harrison/workspace/randsack/ip/randsack/sch/triminv.sym
+// sch_path: /home/harrison/workspace/randsack/ip/randsack/sch/triminv.sch
+module triminv (
+  input wire OUT,
+  input wire IN,
+  input wire TRIM0,
+  input wire TRIM1
+);
+
+wire d0  ;
+wire d1  ;
+wire d2  ;
+wire ts  ;
+
+
+sky130_fd_sc_hd__clkbuf_2
+#(
+)
+x1 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( IN ),
+ .X( ts )
+);
+
+
+sky130_fd_sc_hd__einvn_4
+#(
+)
+x2 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( ts ),
+ .TE_B( TRIM1 ),
+ .Z( d1 )
+);
+
+
+sky130_fd_sc_hd__clkinv_1
+#(
+)
+x3 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( d1 ),
+ .Y( d2 )
+);
+
+
+sky130_fd_sc_hd__einvp_2
+#(
+)
+x4 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( d2 ),
+ .TE( TRIM0 ),
+ .Z( OUT )
+);
+
+
+sky130_fd_sc_hd__clkbuf_1
+#(
+)
+x5 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( ts ),
+ .X( d0 )
+);
+
+
+sky130_fd_sc_hd__einvp_2
+#(
+)
+x6 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( d0 ),
+ .TE( TRIM1 ),
+ .Z( d1 )
+);
+
+
+sky130_fd_sc_hd__einvn_8
+#(
+)
+x7 (
+`ifdef USE_POWER_PINS
+.VGND ( VGND ) ,
+.VNB ( VNB ) ,
+.VPB ( VPB ) ,
+.VPWR ( VPWR ) ,
+`endif
+
+ .A( ts ),
+ .TE_B( TRIM0 ),
+ .Z( OUT )
+);
+
+endmodule
diff --git a/ip/randsack/rtl/collapsering_macro.v b/ip/randsack/rtl/collapsering_macro.v
new file mode 100644
index 0000000..d40bf17
--- /dev/null
+++ b/ip/randsack/rtl/collapsering_macro.v
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: 2021 Harrison Pham
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// SPDX-License-Identifier: Apache-2.0
+
+module collapsering_macro #(
+  parameter TRIM_BITS = 28
+) (
+`ifdef USE_POWER_PINS
+    inout vccd1,
+    inout vssd1,
+`endif
+
+  input start,
+  input [TRIM_BITS-1:0] trim_a,
+  input [TRIM_BITS-1:0] trim_b,
+  input [2:0] clkmux,
+
+  output clk_out
+);
+
+  collapsering ring (
+    .CLKBUFOUT(clk_out),
+    .START(start),
+    .TRIMA(trim_a),
+    .TRIMB(trim_b),
+    .CLKMUX(clkmux)
+  );
+
+endmodule  // collapsering_macro
diff --git a/openlane/collapsering_macro/config.tcl b/openlane/collapsering_macro/config.tcl
new file mode 100755
index 0000000..33506cc
--- /dev/null
+++ b/openlane/collapsering_macro/config.tcl
@@ -0,0 +1,62 @@
+# SPDX-FileCopyrightText: 2020 Efabless Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# SPDX-License-Identifier: Apache-2.0
+
+set script_dir [file dirname [file normalize [info script]]]
+
+set ::env(DESIGN_NAME) collapsering_macro
+
+set ::env(VERILOG_FILES) "\
+	$script_dir/../../ip/randsack/rtl/collapsering_macro.v \
+	$script_dir/../../ip/randsack/rtl/collapsering.v"
+
+# Preserve manually instantiated stdcells.
+set ::env(SYNTH_READ_BLACKBOX_LIB) 1
+
+set ::env(DESIGN_IS_CORE) 0
+
+# The clk_ff0 net has the most flops so use this one for timing constraints.
+# Remaining "clock" signals in this design have manually instantiated clock
+# buffers and no timing relationships so it doesn't really matter.
+# TODO(hdpham): Add additional constraints for remaining clock nets.
+set ::env(CLOCK_PORT) "dummy_not_used"
+set ::env(CLOCK_NET) "ring.clk_ff0"
+set ::env(CLOCK_PERIOD) "10"
+
+set ::env(FP_SIZING) absolute
+set ::env(DIE_AREA) "0 0 50 200"
+
+set ::env(FP_PIN_ORDER_CFG) $script_dir/pin_order.cfg
+
+set ::env(PL_BASIC_PLACEMENT) 1
+set ::env(PL_TARGET_DENSITY) 0.4
+
+# Maximum layer used for routing is metal 4.
+# This is because this macro will be inserted in a top level (user_project_wrapper)
+# where the PDN is planned on metal 5. So, to avoid having shorts between routes
+# in this macro and the top level metal 5 stripes, we have to restrict routes to metal4.
+# TODO(hdpham): Figure out why blocking li1 doesn't work.
+#set ::env(GLB_RT_MINLAYER) 2
+set ::env(GLB_RT_MAXLAYER) 5
+
+# Really force the router to not use li1/met5.
+#set ::env(GLB_RT_OBS) "li1 0 0 50 200, met5 0 0 50 200"
+
+# You can draw more power domains if you need to
+set ::env(VDD_NETS) [list {vccd1}]
+set ::env(GND_NETS) [list {vssd1}]
+
+set ::env(DIODE_INSERTION_STRATEGY) 4
+# If you're going to use multiple power domains, then disable cvc run.
+set ::env(RUN_CVC) 1
diff --git a/openlane/collapsering_macro/pin_order.cfg b/openlane/collapsering_macro/pin_order.cfg
new file mode 100644
index 0000000..f029126
--- /dev/null
+++ b/openlane/collapsering_macro/pin_order.cfg
@@ -0,0 +1,14 @@
+#BUS_SORT
+
+#S
+clkmux.*
+clk_out
+
+#N
+start
+
+#E
+trim_a.*
+
+#W
+trim_b.*