Add ringosc and tweak build
diff --git a/ip/randsack/rtl/collapsering.v b/ip/randsack/rtl/collapsering.v
index 04ce4c9..6c686ad 100644
--- a/ip/randsack/rtl/collapsering.v
+++ b/ip/randsack/rtl/collapsering.v
@@ -106,7 +106,7 @@
 );
 
 
-sky130_fd_sc_hd__clkbuf_4
+sky130_fd_sc_hd__clkbuf_8
 #(
 )
 x35 (
diff --git a/ip/randsack/rtl/collapsering_macro.v b/ip/randsack/rtl/collapsering_macro.v
index fd17492..1b4c50d 100644
--- a/ip/randsack/rtl/collapsering_macro.v
+++ b/ip/randsack/rtl/collapsering_macro.v
@@ -42,6 +42,12 @@
   reg [31:0] clk_cnt;
   reg fake_clk;
 
+  wire [31:0] max_cnt = trim_a + trim_b + clkmux;
+
+  always @(max_cnt) begin
+    $display("collapsering_macro max count = 'h%h", max_cnt);
+  end
+
   initial begin
     clk_cnt <= 0;
     fake_clk <= 1'b0;
@@ -57,7 +63,7 @@
 
   always #5 fake_clk <= ~fake_clk;
 
-  assign clk_out = fake_clk & start & (clk_cnt < 100);
+  assign clk_out = fake_clk & start & (clk_cnt < max_cnt);
 `endif
 
 endmodule // module collapsering_macro
diff --git a/ip/randsack/rtl/digitalcore_macro.v b/ip/randsack/rtl/digitalcore_macro.v
index 9c41fb7..16e2be4 100644
--- a/ip/randsack/rtl/digitalcore_macro.v
+++ b/ip/randsack/rtl/digitalcore_macro.v
@@ -51,7 +51,13 @@
   output ring0_start,
   output [27:0] ring0_trim_a,
   output [27:0] ring0_trim_b,
-  output [2:0] ring0_clkmux
+  output [2:0] ring0_clkmux,
+
+  // ring1 collapsering macro
+  input ring1_clk,
+  output ring1_start,
+  output [25:0] ring1_trim_a,
+  output [2:0] ring1_clkmux
 );
 
   // Filter wishbone accesses from Caravel.
@@ -67,6 +73,8 @@
   parameter UART0_BASE_ADDR   = 32'h3082_0000;
   parameter RING0_ADDR_MASK   = 32'hffff_0000;
   parameter RING0_BASE_ADDR   = 32'h3083_0000;
+  parameter RING1_ADDR_MASK   = 32'hffff_0000;
+  parameter RING1_BASE_ADDR   = 32'h3084_0000;
 
   // Filter addresses from Caravel since we want to be absolutely sure it is
   // selecting us before letting it access the arbiter.  This is mostly needed
@@ -75,7 +83,7 @@
   wire wbs_addr_sel;
   assign wbs_addr_sel = (wbs_adr_i & DTOP_MASK) == DTOP_ADDR;
 
-  wb_mux_3 interconnect (
+  wb_mux_4 interconnect (
     .wbm_adr_i(wbs_adr_i),
     .wbm_dat_i(wbs_dat_i),
     .wbm_dat_o(wbs_dat_o),
@@ -124,7 +132,20 @@
     .wbs2_rty_i(1'b0),
     .wbs2_cyc_o(ring0_cyc_i),
     .wbs2_addr(RING0_BASE_ADDR),
-    .wbs2_addr_msk(RING0_ADDR_MASK)
+    .wbs2_addr_msk(RING0_ADDR_MASK),
+
+    .wbs3_adr_o(ring1_adr_i),
+    .wbs3_dat_i(ring1_dat_o),
+    .wbs3_dat_o(ring1_dat_i),
+    .wbs3_we_o(ring1_we_i),
+    .wbs3_sel_o(ring1_sel_i),
+    .wbs3_stb_o(ring1_stb_i),
+    .wbs3_ack_i(ring1_ack_o),
+    .wbs3_err_i(1'b0),
+    .wbs3_rty_i(1'b0),
+    .wbs3_cyc_o(ring1_cyc_i),
+    .wbs3_addr(RING1_BASE_ADDR),
+    .wbs3_addr_msk(RING1_ADDR_MASK)
   );
 
   // GPIO signals.
@@ -204,6 +225,8 @@
   wire ring0_stb_i;
   wire ring0_ack_o;
   wire [31:0] ring0_dat_o;
+  wire ring0_test_en;
+  wire ring0_test_out;
 
   ring_control #(
     .CLKMUX_BITS(3),
@@ -226,17 +249,59 @@
     .ring_start(ring0_start),
     .ring_trim_a(ring0_trim_a),
     .ring_trim_b(ring0_trim_b),
-    .ring_clkmux(ring0_clkmux)
+    .ring_clkmux(ring0_clkmux),
+
+    .test_en(ring0_test_en),
+    .test_out(ring0_test_out)
+  );
+
+  // RING1 signals.
+  wire [31:0] ring1_adr_i;
+  wire [31:0] ring1_dat_i;
+  wire [3:0] ring1_sel_i;
+  wire ring1_we_i;
+  wire ring1_cyc_i;
+  wire ring1_stb_i;
+  wire ring1_ack_o;
+  wire [31:0] ring1_dat_o;
+  wire ring1_test_en;
+  wire ring1_test_out;
+
+  ring_control #(
+    .CLKMUX_BITS(3),
+    .TRIM_BITS(26)
+  ) ring1 (
+    .wb_clk_i(wb_clk_i),
+    .wb_rst_i(wb_rst_i),
+
+    .wb_stb_i(ring1_stb_i),
+    .wb_cyc_i(ring1_cyc_i),
+    .wb_we_i(ring1_we_i),
+    .wb_sel_i(ring1_sel_i),
+    .wb_dat_i(ring1_dat_i),
+    .wb_adr_i(ring1_adr_i),
+
+    .wb_ack_o(ring1_ack_o),
+    .wb_dat_o(ring1_dat_o),
+
+    .ring_clk(ring1_clk),
+    .ring_start(ring1_start),
+    .ring_trim_a(ring1_trim_a),
+    .ring_trim_b(),
+    .ring_clkmux(ring1_clkmux),
+
+    .test_en(ring1_test_en),
+    .test_out(ring1_test_out)
   );
 
   // Connect up external ports.
   assign gpio_in = io_in[37:6];
-  assign uart_rx = io_in[2];
+  assign uart_rx = io_in[35];
 
-  assign io_out[5:0] = {1'b0, uart_tx, 4'b0};
-  assign io_out[37:6] = gpio_out;
-  assign io_oeb[5:0] = {1'b1, ~uart_enabled, 4'b1};
-  assign io_oeb[37:6] = gpio_oeb;
+  assign io_out[5:0] = 6'b0;
+  assign io_out[37:6] = gpio_out | {uart_tx & uart_enabled, 4'b0, ring0_test_out, 26'b0};
+  assign io_oeb[5:0] = 6'b0;
+  assign io_oeb[37:6] = gpio_oeb & {~uart_enabled, {4{1'b1}}, ~ring0_test_en, {26{1'b1}}};
 
   assign la_data_out = 128'b0;
 
diff --git a/ip/randsack/rtl/ring_control.v b/ip/randsack/rtl/ring_control.v
index 5efe222..cc01661 100644
--- a/ip/randsack/rtl/ring_control.v
+++ b/ip/randsack/rtl/ring_control.v
@@ -36,7 +36,10 @@
   output reg ring_start,
   output reg [TRIM_BITS-1:0] ring_trim_a,
   output reg [TRIM_BITS-1:0] ring_trim_b,
-  output reg [CLKMUX_BITS-1:0] ring_clkmux
+  output reg [CLKMUX_BITS-1:0] ring_clkmux,
+
+  output reg test_en,
+  output test_out
 );
   // Register addresses.
   localparam COUNT_VALUE_ADDR = 8'h00;
@@ -59,6 +62,9 @@
   reg counter_resetb;
   reg [16:0] counter_value;
 
+  // Test port mux.
+  assign test_out = test_en ? ring_clk : 1'b0;
+
   // Ring oscillator counter.
   // NOTE: This reset is pretty nasty, but the idea is we have full control
   // over the reset timing so we don't need to synchronize it.
@@ -77,9 +83,10 @@
       wb_dat_o <= 32'h0;
       counter_resetb <= 1'b0;
       ring_start <= 1'b0;
-      ring_clkmux <= {CLKMUX_BITS{1'b1}};
-      ring_trim_a <= {TRIM_BITS{1'b1}};
-      ring_trim_b <= {TRIM_BITS{1'b1}};
+      ring_clkmux <= {CLKMUX_BITS{1'b0}};
+      ring_trim_a <= {TRIM_BITS{1'b0}};
+      ring_trim_b <= {TRIM_BITS{1'b0}};
+      test_en <= 1'b0;
     end else begin
       wb_ack_o <= 1'b0;
 
@@ -89,6 +96,7 @@
         if (control_sel) begin
           counter_resetb <= ~wb_dat_i[0];
           ring_start <= wb_dat_i[1];
+          test_en <= wb_dat_i[2];
           ring_clkmux <= wb_dat_i[CLKMUX_BITS-1+8:8];
         end else if (trima_sel) begin
           ring_trim_a <= wb_dat_i[TRIM_BITS-1:0];
diff --git a/ip/randsack/rtl/ringosc_macro.v b/ip/randsack/rtl/ringosc_macro.v
new file mode 100644
index 0000000..6a7011c
--- /dev/null
+++ b/ip/randsack/rtl/ringosc_macro.v
@@ -0,0 +1,74 @@
+// 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 ringosc_macro #(
+  parameter TRIM_BITS = 26
+) (
+`ifdef USE_POWER_PINS
+    inout vccd1,
+    inout vssd1,
+`endif
+
+  input start,
+  input [TRIM_BITS-1:0] trim_a,
+  input [2:0] clkmux,
+
+  output clk_out
+);
+
+  wire [1:0] clockp;
+  wire clk = clockp[0];
+
+  ring_osc2x13 ring (
+    .reset(~start),
+    .trim(trim_a),
+    .clockp(clockp)
+  );
+
+  // Clock dividers.
+  reg s0;
+  always @(posedge clk or negedge start) begin
+    if (!start) begin
+      s0 <= 1'b0;
+    end else begin
+      s0 <= ~s0;
+    end
+  end
+
+  reg s1;
+  always @(posedge s0 or negedge start) begin
+    if (!start) begin
+      s1 <= 1'b0;
+    end else begin
+      s1 <= ~s1;
+    end
+  end
+
+  reg s2;
+  always @(posedge s1 or negedge start) begin
+    if (!start) begin
+      s2 <= 1'b0;
+    end else begin
+      s2 <= ~s2;
+    end
+  end
+
+  assign clk_out = (clkmux == 3'b000) ? clk :
+                   (clkmux == 3'b001) ? s0  :
+                   (clkmux == 3'b010) ? s1  :
+                   (clkmux == 3'b011) ? s2  :
+                   clk;
+
+endmodule // module ringosc_macro
diff --git a/ip/randsack/sch/collapsering.sch b/ip/randsack/sch/collapsering.sch
index 2550fff..982a316 100644
--- a/ip/randsack/sch/collapsering.sch
+++ b/ip/randsack/sch/collapsering.sch
@@ -305,7 +305,7 @@
 C {devices/title-2.sym} 0 -40 0 0 {name=l26 author="Harrison Pham" rev=1.0}
 C {devices/lab_wire.sym} 3100 -1140 0 0 {name=l18 sig_type=std_logic lab=outb}
 C {devices/lab_wire.sym} 3100 -1530 0 0 {name=l9 sig_type=std_logic lab=outa}
-C {sky130_stdcells/clkbuf_4.sym} 3160 -800 0 0 {name=x35 VGND=VGND VNB=VNB VPB=VPB VPWR=VPWR prefix=sky130_fd_sc_hd__ }
+C {sky130_stdcells/clkbuf_8.sym} 3160 -800 0 0 {name=x35 VGND=VGND VNB=VNB VPB=VPB VPWR=VPWR prefix=sky130_fd_sc_hd__ }
 C {devices/opin.sym} 3230 -800 0 0 {name=p1 lab=CLKBUFOUT}
 C {devices/ipin.sym} 110 -1690 0 0 {name=p2 lab=START}
 C {devices/lab_pin.sym} 400 -1660 0 0 {name=l1 sig_type=std_logic lab=TRIMA[0]}
diff --git a/ip/third_party/caravel/ring_osc2x13.v b/ip/third_party/caravel/ring_osc2x13.v
new file mode 100644
index 0000000..f20110e
--- /dev/null
+++ b/ip/third_party/caravel/ring_osc2x13.v
@@ -0,0 +1,250 @@
+// 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
+
+`default_nettype none
+// Tunable ring oscillator---synthesizable (physical) version.
+//
+// NOTE:  This netlist cannot be simulated correctly due to lack
+// of accurate timing in the digital cell verilog models.
+
+module delay_stage(in, trim, out);
+    input in;
+    input [1:0] trim;
+    output out;
+
+    wire d0, d1, d2, ts;
+
+    sky130_fd_sc_hd__clkbuf_2 delaybuf0 (
+	.A(in),
+	.X(ts)
+    );
+
+    sky130_fd_sc_hd__clkbuf_1 delaybuf1 (
+	.A(ts),
+	.X(d0)
+    );
+
+    sky130_fd_sc_hd__einvp_2 delayen1 (
+	.A(d0),
+	.TE(trim[1]),
+	.Z(d1)
+    );
+
+    sky130_fd_sc_hd__einvn_4 delayenb1 (
+	.A(ts),
+	.TE_B(trim[1]),
+	.Z(d1)
+    );
+
+    sky130_fd_sc_hd__clkinv_1 delayint0 (
+	.A(d1),
+	.Y(d2)
+    );
+
+    sky130_fd_sc_hd__einvp_2 delayen0 (
+	.A(d2),
+	.TE(trim[0]),
+	.Z(out)
+    );
+
+    sky130_fd_sc_hd__einvn_8 delayenb0 (
+	.A(ts),
+	.TE_B(trim[0]),
+	.Z(out)
+    );
+
+endmodule
+
+module start_stage(in, trim, reset, out);
+    input in;
+    input [1:0] trim;
+    input reset;
+    output out;
+
+    wire d0, d1, d2, ctrl0, one;
+
+    sky130_fd_sc_hd__clkbuf_1 delaybuf0 (
+	.A(in),
+	.X(d0)
+    );
+
+    sky130_fd_sc_hd__einvp_2 delayen1 (
+	.A(d0),
+	.TE(trim[1]),
+	.Z(d1)
+    );
+
+    sky130_fd_sc_hd__einvn_4 delayenb1 (
+	.A(in),
+	.TE_B(trim[1]),
+	.Z(d1)
+    );
+
+    sky130_fd_sc_hd__clkinv_1 delayint0 (
+	.A(d1),
+	.Y(d2)
+    );
+
+    sky130_fd_sc_hd__einvp_2 delayen0 (
+	.A(d2),
+	.TE(trim[0]),
+	.Z(out)
+    );
+
+    sky130_fd_sc_hd__einvn_8 delayenb0 (
+	.A(in),
+	.TE_B(ctrl0),
+	.Z(out)
+    );
+
+    sky130_fd_sc_hd__einvp_1 reseten0 (
+	.A(one),
+	.TE(reset),
+	.Z(out)
+    );
+
+    sky130_fd_sc_hd__or2_2 ctrlen0 (
+	.A(reset),
+	.B(trim[0]),
+	.X(ctrl0)
+    );
+
+    sky130_fd_sc_hd__conb_1 const1 (
+	.HI(one),
+	.LO()
+    );
+
+endmodule
+
+// Ring oscillator with 13 stages, each with two trim bits delay
+// (see above).  Trim is not binary:  For trim[1:0], lower bit
+// trim[0] is primary trim and must be applied first;  upper
+// bit trim[1] is secondary trim and should only be applied
+// after the primary trim is applied, or it has no effect.
+//
+// Total effective number of inverter stages in this oscillator
+// ranges from 13 at trim 0 to 65 at trim 24.  The intention is
+// to cover a range greater than 2x so that the midrange can be
+// reached over all PVT conditions.
+//
+// Frequency of this ring oscillator under SPICE simulations at
+// nominal PVT is maximum 214 MHz (trim 0), minimum 90 MHz (trim 24).
+
+module ring_osc2x13(reset, trim, clockp);
+    input reset;
+    input [25:0] trim;
+    output[1:0] clockp;
+
+`ifdef FUNCTIONAL	// i.e., behavioral model below
+
+    reg [1:0] clockp;
+    reg hiclock;
+    integer i;
+    real delay;
+    wire [5:0] bcount;
+
+    assign bcount = trim[0] + trim[1] + trim[2]
+		+ trim[3] + trim[4] + trim[5] + trim[6] + trim[7]
+		+ trim[8] + trim[9] + trim[10] + trim[11] + trim[12]
+		+ trim[13] + trim[14] + trim[15] + trim[16] + trim[17]
+		+ trim[18] + trim[19] + trim[20] + trim[21] + trim[22]
+		+ trim[23] + trim[24] + trim[25];
+
+    initial begin
+	hiclock <= 1'b0;
+	delay = 3.0;
+    end
+
+    // Fastest operation is 214 MHz = 4.67ns
+    // Delay per trim is 0.02385
+    // Run "hiclock" at 2x this rate, then use positive and negative
+    // edges to derive the 0 and 90 degree phase clocks.
+
+    always #delay begin
+	hiclock <= (hiclock === 1'b0);
+    end
+
+    always @(trim) begin
+    	// Implement trim as a variable delay, one delay per trim bit
+	delay = 1.168 + 0.012 * $itor(bcount);
+    end
+
+    always @(posedge hiclock or posedge reset) begin
+	if (reset == 1'b1) begin
+	    clockp[0] <= 1'b0;
+	end else begin
+	    clockp[0] <= (clockp[0] === 1'b0);
+	end
+    end
+
+    always @(negedge hiclock or posedge reset) begin
+	if (reset == 1'b1) begin
+	    clockp[1] <= 1'b0;
+	end else begin
+	    clockp[1] <= (clockp[1] === 1'b0);
+	end
+    end
+
+`else 			// !FUNCTIONAL;  i.e., gate level netlist below
+
+    wire [1:0] clockp;
+    wire [12:0] d;
+    wire [1:0] c;
+
+    // Main oscillator loop stages
+ 
+    genvar i;
+    generate
+	for (i = 0; i < 12; i = i + 1) begin : dstage
+	    delay_stage id (
+		.in(d[i]),
+		.trim({trim[i+13], trim[i]}),
+		.out(d[i+1])
+	    );
+	end
+    endgenerate
+
+    // Reset/startup stage
+ 
+    start_stage iss (
+	.in(d[12]),
+	.trim({trim[25], trim[12]}),
+	.reset(reset),
+	.out(d[0])
+    );
+
+    // Buffered outputs a 0 and 90 degrees phase (approximately)
+
+    sky130_fd_sc_hd__clkinv_2 ibufp00 (
+	.A(d[0]),
+	.Y(c[0])
+    );
+    sky130_fd_sc_hd__clkinv_8 ibufp01 (
+	.A(c[0]),
+	.Y(clockp[0])
+    );
+    sky130_fd_sc_hd__clkinv_2 ibufp10 (
+	.A(d[6]),
+	.Y(c[1])
+    );
+    sky130_fd_sc_hd__clkinv_8 ibufp11 (
+	.A(c[1]),
+	.Y(clockp[1])
+    );
+
+`endif // !FUNCTIONAL
+
+endmodule
+`default_nettype wire
diff --git a/openlane/collapsering_macro/config.tcl b/openlane/collapsering_macro/config.tcl
index f73eedc..76760b1 100755
--- a/openlane/collapsering_macro/config.tcl
+++ b/openlane/collapsering_macro/config.tcl
@@ -26,11 +26,12 @@
 
 # Disable optimizations and CTS to preserve our hand picked stdcells.
 set ::env(SYNTH_BUFFERING) 0
+set ::env(SYNTH_SIZING) 0
 set ::env(SYNTH_SHARE_RESOURCES) 0
+set ::env(CLOCK_TREE_SYNTH) 0
 set ::env(PL_RESIZER_DESIGN_OPTIMIZATIONS) 0
 set ::env(PL_RESIZER_TIMING_OPTIMIZATIONS) 0
 set ::env(PL_OPENPHYSYN_OPTIMIZATIONS) 0
-set ::env(CLOCK_TREE_SYNTH) 0
 set ::env(GLB_RESIZER_TIMING_OPTIMIZATIONS) 0
 
 set ::env(DESIGN_IS_CORE) 0
@@ -44,12 +45,13 @@
 set ::env(CLOCK_PERIOD) "10"
 
 set ::env(FP_SIZING) absolute
+# 85 85 is optimal but wont fit into PDN
 set ::env(DIE_AREA) "0 0 50 150"
 
 set ::env(FP_PIN_ORDER_CFG) $script_dir/pin_order.cfg
 
 set ::env(PL_BASIC_PLACEMENT) 1
-set ::env(PL_TARGET_DENSITY) 0.5
+set ::env(PL_TARGET_DENSITY) 0.75
 
 # Maximum layer used for routing is metal 4.
 # This is because this macro will be inserted in a top level (user_project_wrapper)
diff --git a/openlane/collapsering_macro/pin_order.cfg b/openlane/collapsering_macro/pin_order.cfg
index 46201b2..1a9c694 100644
--- a/openlane/collapsering_macro/pin_order.cfg
+++ b/openlane/collapsering_macro/pin_order.cfg
@@ -9,6 +9,6 @@
 
 #E
 trim_a.*
+trim_b.*
 
 #W
-trim_b.*
diff --git a/openlane/digitalcore_macro/base.sdc b/openlane/digitalcore_macro/base.sdc
index 5efe131..c1a7a33 100644
--- a/openlane/digitalcore_macro/base.sdc
+++ b/openlane/digitalcore_macro/base.sdc
@@ -15,12 +15,15 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-set ::env(WB_CLOCK_PERIOD)    "10"
+set ::env(WB_CLOCK_PERIOD)    "15"
 set ::env(WB_CLOCK_PORT)      "wb_clk_i"
 
-set ::env(RING0_CLOCK_PERIOD) "10"
+set ::env(RING0_CLOCK_PERIOD) "15"
 set ::env(RING0_CLOCK_PORT)   "ring0_clk"
 
+set ::env(RING1_CLOCK_PERIOD) "15"
+set ::env(RING1_CLOCK_PORT)   "ring1_clk"
+
 if {[info exists ::env(WB_CLOCK_PORT)] && $::env(WB_CLOCK_PORT) != ""} {
     create_clock [get_ports $::env(WB_CLOCK_PORT)]  -name $::env(WB_CLOCK_PORT)  -period $::env(CLOCK_PERIOD)
 } else {
@@ -55,3 +58,4 @@
 # Extra clocks for macros.
 # NOTE: These don't need any input/output delays since this design just uses it for clock counting.
 create_clock [get_ports $::env(RING0_CLOCK_PORT)] -name $::env(RING0_CLOCK_PORT) -period $::env(RING0_CLOCK_PERIOD)
+create_clock [get_ports $::env(RING1_CLOCK_PORT)] -name $::env(RING1_CLOCK_PORT) -period $::env(RING1_CLOCK_PERIOD)
diff --git a/openlane/digitalcore_macro/config.tcl b/openlane/digitalcore_macro/config.tcl
index 76c41aa..984323c 100755
--- a/openlane/digitalcore_macro/config.tcl
+++ b/openlane/digitalcore_macro/config.tcl
@@ -22,7 +22,7 @@
 	$script_dir/../../ip/randsack/rtl/digitalcore_macro.v \
 	$script_dir/../../ip/third_party/picorv32_wb/gpio32_wb.v \
 	$script_dir/../../ip/third_party/picorv32_wb/simpleuart_div16_wb.v \
-	$script_dir/../../ip/third_party/verilog-wishbone/rtl/wb_mux_3.v \
+	$script_dir/../../ip/third_party/verilog-wishbone/rtl/wb_mux_4.v \
 	$script_dir/../../ip/randsack/rtl/ring_control.v"
 
 set ::env(DESIGN_IS_CORE) 0
@@ -31,8 +31,8 @@
 set ::env(BASE_SDC_FILE) "$script_dir/base.sdc"
 
 # NOTE: Make sure to add all clocks manually to base.sdc!
-set ::env(CLOCK_PORT) "wb_clk_i ring0_clk"
-set ::env(CLOCK_PERIOD) "10"
+set ::env(CLOCK_PORT) "wb_clk_i ring0_clk ring1_clk"
+set ::env(CLOCK_PERIOD) "15"
 
 set ::env(FP_SIZING) absolute
 set ::env(DIE_AREA) "0 0 500 500"
diff --git a/openlane/ringosc_macro/config.tcl b/openlane/ringosc_macro/config.tcl
new file mode 100755
index 0000000..60d4a8f
--- /dev/null
+++ b/openlane/ringosc_macro/config.tcl
@@ -0,0 +1,70 @@
+# 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) ringosc_macro
+
+set ::env(VERILOG_FILES) "\
+	$script_dir/../../ip/randsack/rtl/ringosc_macro.v \
+	$script_dir/../../ip/third_party/caravel/ring_osc2x13.v"
+
+# Preserve manually instantiated stdcells.
+set ::env(SYNTH_READ_BLACKBOX_LIB) 1
+
+# Disable optimizations and CTS to preserve our hand picked stdcells.
+set ::env(SYNTH_BUFFERING) 0
+set ::env(SYNTH_SIZING) 0
+set ::env(SYNTH_SHARE_RESOURCES) 0
+set ::env(CLOCK_TREE_SYNTH) 0
+set ::env(PL_RESIZER_DESIGN_OPTIMIZATIONS) 0
+set ::env(PL_RESIZER_TIMING_OPTIMIZATIONS) 0
+set ::env(PL_OPENPHYSYN_OPTIMIZATIONS) 0
+set ::env(GLB_RESIZER_TIMING_OPTIMIZATIONS) 0
+
+set ::env(DESIGN_IS_CORE) 0
+
+# TODO(hdpham): Properly disable STA.
+set ::env(CLOCK_PORT) "clk_out"
+# set ::env(CLOCK_NET) "ring.clk_ff0"
+set ::env(CLOCK_PERIOD) "10"
+
+set ::env(FP_SIZING) absolute
+# 70 70 is optimal but won't fit in PDN.
+set ::env(DIE_AREA) "0 0 50 150"
+
+set ::env(FP_PIN_ORDER_CFG) $script_dir/pin_order.cfg
+
+set ::env(PL_BASIC_PLACEMENT) 1
+set ::env(PL_TARGET_DENSITY) 0.75
+
+# 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/ringosc_macro/pin_order.cfg b/openlane/ringosc_macro/pin_order.cfg
new file mode 100644
index 0000000..dbbb80f
--- /dev/null
+++ b/openlane/ringosc_macro/pin_order.cfg
@@ -0,0 +1,13 @@
+#BUS_SORT
+
+#S
+clkmux.*
+clk_out
+start
+
+#N
+
+#E
+trim_a.*
+
+#W
diff --git a/openlane/user_project_wrapper/config.tcl b/openlane/user_project_wrapper/config.tcl
index 80814ac..7cef48f 100755
--- a/openlane/user_project_wrapper/config.tcl
+++ b/openlane/user_project_wrapper/config.tcl
@@ -53,17 +53,20 @@
 	$::env(CARAVEL_ROOT)/verilog/rtl/defines.v \
 	$script_dir/../../verilog/rtl/user_proj_example.v \
 	$script_dir/../../ip/randsack/rtl/digitalcore_macro.v \
-	$script_dir/../../ip/randsack/rtl/collapsering_macro.v"
+	$script_dir/../../ip/randsack/rtl/collapsering_macro.v \
+	$script_dir/../../ip/randsack/rtl/ringosc_macro.v"
 
 set ::env(EXTRA_LEFS) "\
 	$script_dir/../../lef/user_proj_example.lef \
 	$script_dir/../../lef/digitalcore_macro.lef \
-	$script_dir/../../lef/collapsering_macro.lef"
+	$script_dir/../../lef/collapsering_macro.lef \
+	$script_dir/../../lef/ringosc_macro.lef"
 
 set ::env(EXTRA_GDS_FILES) "\
 	$script_dir/../../gds/user_proj_example.gds \
 	$script_dir/../../gds/digitalcore_macro.gds \
-	$script_dir/../../gds/collapsering_macro.gds"
+	$script_dir/../../gds/collapsering_macro.gds \
+	$script_dir/../../gds/ringosc_macro.gds"
 
 set ::env(GLB_RT_MAXLAYER) 5
 
diff --git a/openlane/user_project_wrapper/macro.cfg b/openlane/user_project_wrapper/macro.cfg
index 732f5ab..e02b4fd 100644
--- a/openlane/user_project_wrapper/macro.cfg
+++ b/openlane/user_project_wrapper/macro.cfg
@@ -1,2 +1,3 @@
 digitalcore 1000 500 N
-collapsering0 700 620 N
+ring0 800 620 N
+ring1 800 800 N
diff --git a/verilog/dv/randsack_netlists.v b/verilog/dv/randsack_netlists.v
index 2eb9750..68f604b 100644
--- a/verilog/dv/randsack_netlists.v
+++ b/verilog/dv/randsack_netlists.v
@@ -25,8 +25,9 @@
     `include "user_project_wrapper.v"
     `include "digitalcore_macro.v"
     `include "collapsering_macro.v"
+    `include "ringosc_macro.v"
     `include "ring_control.v"
     `include "picorv32_wb/gpio32_wb.v"
     `include "picorv32_wb/simpleuart_div16_wb.v"
-    `include "verilog-wishbone/rtl/wb_mux_3.v"
+    `include "verilog-wishbone/rtl/wb_mux_4.v"
 `endif
diff --git a/verilog/dv/randsack_regrw_directed/randsack_regrw_directed.c b/verilog/dv/randsack_regrw_directed/randsack_regrw_directed.c
index 2218b98..ff29a94 100644
--- a/verilog/dv/randsack_regrw_directed/randsack_regrw_directed.c
+++ b/verilog/dv/randsack_regrw_directed/randsack_regrw_directed.c
@@ -35,6 +35,7 @@
 #define RANDSACK_RING_CONTROL_ADDR  0x04
 #define RANDSACK_RING_TRIMA_ADDR    0x08
 #define RANDSACK_RING_TRIMB_ADDR    0x0c
+#define RANDSACK_RING_CLKMUX_OFFSET 8
 
 #define RANDSACK_RING_CONTROL_RESET_MASK (1 << 0)
 #define RANDSACK_RING_CONTROL_START_MASK (1 << 1)
@@ -80,18 +81,24 @@
   REG(RANDSACK_GPIO0_BASE + RANDSACK_GPIO_DATA) = 0x55550000;
 
   // Reset ring osc.
+  REG(RANDSACK_RING0_BASE + RANDSACK_RING_TRIMA_ADDR) = 50;
+  REG(RANDSACK_RING0_BASE + RANDSACK_RING_TRIMB_ADDR) = 60;
   REG(RANDSACK_RING0_BASE + RANDSACK_RING_CONTROL_ADDR) = RANDSACK_RING_CONTROL_RESET_MASK;
   REG(RANDSACK_RING0_BASE + RANDSACK_RING_CONTROL_ADDR) = 0;
 
   // Start ring osc.
-  REG(RANDSACK_RING0_BASE + RANDSACK_RING_CONTROL_ADDR) = RANDSACK_RING_CONTROL_START_MASK;
+  REG(RANDSACK_RING0_BASE + RANDSACK_RING_CONTROL_ADDR) = RANDSACK_RING_CONTROL_START_MASK + (4 << RANDSACK_RING_CLKMUX_OFFSET);
 
   REG(RANDSACK_GPIO0_BASE + RANDSACK_GPIO_DATA) = 0xaaaa0000;
 
   // Wait for valid value.
-  while (REG(RANDSACK_RING0_BASE + RANDSACK_RING_COUNT_ADDR) < 100) {
+  while (REG(RANDSACK_RING0_BASE + RANDSACK_RING_COUNT_ADDR) < 114) {
     REG(RANDSACK_GPIO0_BASE + RANDSACK_GPIO_DATA) = 0xdead0000;
   }
 
-  REG(RANDSACK_GPIO0_BASE + RANDSACK_GPIO_DATA) = 0xfeed0000;
+  // Check to make sure the counter stopped.  Our CPU is really slow so this
+  // is a safe check.
+  if (REG(RANDSACK_RING0_BASE + RANDSACK_RING_COUNT_ADDR) == 114) {
+    REG(RANDSACK_GPIO0_BASE + RANDSACK_GPIO_DATA) = 0xfeed0000;
+  }
 }