Add WIP dtop and ring controller
diff --git a/ip/randsack/rtl/dtop.v b/ip/randsack/rtl/dtop.v
new file mode 100644
index 0000000..86e4b5b
--- /dev/null
+++ b/ip/randsack/rtl/dtop.v
@@ -0,0 +1,250 @@
+// Randsack digital top.
+//
+// SPDX-FileCopyrightText: (c) 2021 Harrison Pham <harrison@harrisonpham.com>
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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.
+
+module dtop (
+`ifdef USE_POWER_PINS
+  inout vccd1,
+  inout vssd1,
+`endif
+
+  // Wishbone Slave ports (WB MI A)
+  input wb_clk_i,
+  input wb_rst_i,
+  input wbs_stb_i,
+  input wbs_cyc_i,
+  input wbs_we_i,
+  input [3:0] wbs_sel_i,
+  input [31:0] wbs_dat_i,
+  input [31:0] wbs_adr_i,
+  output wbs_ack_o,
+  output [31:0] wbs_dat_o,
+
+  // Logic Analyzer Signals
+  input  [127:0] la_data_in,
+  output [127:0] la_data_out,
+  input  [127:0] la_oenb,
+
+  // IOs
+  input  [`MPRJ_IO_PADS-1:0] io_in,
+  output [`MPRJ_IO_PADS-1:0] io_out,
+  output [`MPRJ_IO_PADS-1:0] io_oeb,
+
+  // IRQ
+  output [2:0] irq
+);
+
+  // Filter wishbone accesses from Caravel.
+  parameter DTOP_MASK    = 32'hff00_0000;
+  parameter DTOP_ADDR    = 32'h3000_0000;
+
+  // Peripherals.
+  parameter GPIO0_ADDR_MASK   = 32'hffff_0000;
+  parameter GPIO0_BASE_ADDR   = 32'h3080_0000;
+  parameter PWM0_ADDR_MASK    = 32'hffff_0000;
+  parameter PWM0_BASE_ADDR    = 32'h3081_0000;
+  parameter UART0_ADDR_MASK   = 32'hffff_0000;
+  parameter UART0_BASE_ADDR   = 32'h3082_0000;
+  parameter RING0_ADDR_MASK   = 32'hffff_0000;
+  parameter RING0_BASE_ADDR   = 32'h3083_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
+  // because the wb_intercon.v implementation in Caravel doesn't corrently
+  // filter the wb_cyc_i signal to slaves.
+  wire wbs_addr_sel;
+  assign wbs_addr_sel = (wbs_adr_i & DTOP_MASK) == DTOP_ADDR;
+
+  wb_mux_3 interconnect (
+    .wbm_adr_i(wbs_adr_i),
+    .wbm_dat_i(wbs_dat_i),
+    .wbm_dat_o(wbs_dat_o),
+    .wbm_we_i(wbs_we_i & wbs_addr_sel),
+    .wbm_sel_i(wbs_sel_i),
+    .wbm_stb_i(wbs_stb_i & wbs_addr_sel),
+    .wbm_ack_o(wbs_ack_o),
+    .wbm_err_o(),
+    .wbm_rty_o(),
+    .wbm_cyc_i(wbs_cyc_i & wbs_addr_sel),
+
+    .wbs0_adr_o(gpio_adr_i),
+    .wbs0_dat_i(gpio_dat_o),
+    .wbs0_dat_o(gpio_dat_i),
+    .wbs0_we_o(gpio_we_i),
+    .wbs0_sel_o(gpio_sel_i),
+    .wbs0_stb_o(gpio_stb_i),
+    .wbs0_ack_i(gpio_ack_o),
+    .wbs0_err_i(1'b0),
+    .wbs0_rty_i(1'b0),
+    .wbs0_cyc_o(gpio_cyc_i),
+    .wbs0_addr(GPIO0_BASE_ADDR),
+    .wbs0_addr_msk(GPIO0_ADDR_MASK),
+
+    .wbs1_adr_o(uart_adr_i),
+    .wbs1_dat_i(uart_dat_o),
+    .wbs1_dat_o(uart_dat_i),
+    .wbs1_we_o(uart_we_i),
+    .wbs1_sel_o(uart_sel_i),
+    .wbs1_stb_o(uart_stb_i),
+    .wbs1_ack_i(uart_ack_o),
+    .wbs1_err_i(1'b0),
+    .wbs1_rty_i(1'b0),
+    .wbs1_cyc_o(uart_cyc_i),
+    .wbs1_addr(UART0_BASE_ADDR),
+    .wbs1_addr_msk(UART0_ADDR_MASK),
+
+    .wbs2_adr_o(ring0_adr_i),
+    .wbs2_dat_i(ring0_dat_o),
+    .wbs2_dat_o(ring0_dat_i),
+    .wbs2_we_o(ring0_we_i),
+    .wbs2_sel_o(ring0_sel_i),
+    .wbs2_stb_o(ring0_stb_i),
+    .wbs2_ack_i(ring0_ack_o),
+    .wbs2_err_i(1'b0),
+    .wbs2_rty_i(1'b0),
+    .wbs2_cyc_o(ring0_cyc_i),
+    .wbs2_addr(RING0_BASE_ADDR),
+    .wbs2_addr_msk(RING0_ADDR_MASK),
+  );
+
+  // GPIO signals.
+  wire [31:0] gpio_adr_i;
+  wire [31:0] gpio_dat_i;
+  wire [3:0] gpio_sel_i;
+  wire gpio_we_i;
+  wire gpio_cyc_i;
+  wire gpio_stb_i;
+  wire gpio_ack_o;
+  wire [31:0] gpio_dat_o;
+
+  wire [31:0] gpio_in;
+  wire [31:0] gpio_out;
+  wire [31:0] gpio_oeb;
+
+  gpio32_wb gpio0 (
+    .wb_clk_i(wb_clk_i),
+    .wb_rst_i(wb_rst_i),
+
+    .wb_dat_i(gpio_dat_i),
+    .wb_adr_i(gpio_adr_i),
+    .wb_sel_i(gpio_sel_i),
+    .wb_cyc_i(gpio_cyc_i),
+    .wb_stb_i(gpio_stb_i),
+    .wb_we_i(gpio_we_i),
+
+    .wb_dat_o(gpio_dat_o),
+    .wb_ack_o(gpio_ack_o),
+
+    .gpio_in(gpio_in),
+    .gpio_out(gpio_out),
+    .gpio_oeb(gpio_oeb)
+  );
+
+  // UART signals.
+  wire [31:0] uart_adr_i;
+  wire [31:0] uart_dat_i;
+  wire [3:0] uart_sel_i;
+  wire uart_we_i;
+  wire uart_cyc_i;
+  wire uart_stb_i;
+  wire uart_ack_o;
+  wire [31:0] uart_dat_o;
+
+  wire uart_enabled;
+  wire uart_tx;
+  wire uart_rx;
+
+  simpleuart_wb  #(
+    .BASE_ADR(UART0_BASE_ADDR)
+  ) uart0 (
+    .wb_clk_i(wb_clk_i),
+    .wb_rst_i(wb_rst_i),
+
+    .wb_adr_i(uart_adr_i),
+    .wb_dat_i(uart_dat_i),
+    .wb_sel_i(uart_sel_i),
+    .wb_we_i(uart_we_i),
+    .wb_cyc_i(uart_cyc_i),
+
+    .wb_stb_i(uart_stb_i),
+    .wb_ack_o(uart_ack_o),
+    .wb_dat_o(uart_dat_o),
+
+    .uart_enabled(uart_enabled),
+    .ser_tx(uart_tx),
+    .ser_rx(uart_rx)
+  );
+
+  // RING0 signals.
+  wire [31:0] ring0_adr_i;
+  wire [31:0] ring0_dat_i;
+  wire [3:0] ring0_sel_i;
+  wire ring0_we_i;
+  wire ring0_cyc_i;
+  wire ring0_stb_i;
+  wire ring0_ack_o;
+  wire [31:0] ring0_dat_o;
+
+  wire ring0_clk;
+  wire ring0_start;
+  wire [27:0] ring0_trim_a;
+  wire [27:0] ring0_trim_b;
+  wire [2:0] ring0_clkmux;
+
+  ring_control #(
+    .CLKMUX_BITS(3),
+    .TRIM_BITS(28)
+  ) ring0 (
+    .wb_clk_i(wb_clk_i),
+    .wb_rst_i(wb_rst_i),
+
+    .wbs_stb_i(ring0_stb_i),
+    .wbs_cyc_i(ring0_cyc_i),
+    .wbs_we_i(ring0_we_i),
+    .wbs_sel_i(ring0_sel_i),
+    .wbs_dat_i(ring0_dat_i),
+    .wbs_adr_i(ring0_adr_i),
+
+    .wbs_ack_o(ring0_ack_o),
+    .wbs_dat_o(ring0_dat_o),
+
+    .ring_clk(ring0_clk),
+    .ring_start(ring0_start),
+    .ring_trim_a(ring0_trim_a),
+    .ring_trim_b(ring0_trim_b),
+    .ring_clkmux(ring0_clkmux)
+  );
+
+  collapsering_macro collapsering0 (
+    .CLKBUFOUT(ring0_clk),
+    .START(ring0_start),
+    .TRIMA(ring0_trim_a),
+    .TRIMB(ring0_trim_b),
+    .CLKMUX(ring0_clkmux)
+  );
+
+  // Connect up external ports.
+  assign gpio_in = io_in[37:6];
+  assign io_out[5:0] = 6'b0;
+  assign io_out[37:6] = gpio_out;
+  assign io_oeb[5:0] = {6{1'b1}};
+  assign io_oeb[37:6] = gpio_oeb;
+
+  assign la_data_out = 128'b0;
+
+  assign irq = 3'b0;
+
+endmodule // module dtop
diff --git a/ip/randsack/rtl/ring_control.v b/ip/randsack/rtl/ring_control.v
new file mode 100644
index 0000000..002b918
--- /dev/null
+++ b/ip/randsack/rtl/ring_control.v
@@ -0,0 +1,105 @@
+// Ring oscillator controller.
+//
+// SPDX-FileCopyrightText: (c) 2021 Harrison Pham <harrison@harrisonpham.com>
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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.
+
+module ring_control #(
+  parameter CLKMUX_BITS = 3,
+  parameter TRIM_BITS = 28
+) (
+  input wb_clk_i,
+  input wb_rst_i,
+
+  input wbs_stb_i,
+  input wbs_cyc_i,
+  input wbs_we_i,
+  input [3:0] wbs_sel_i,
+  input [31:0] wbs_dat_i,
+  input [31:0] wbs_adr_i,
+
+  output reg wbs_ack_o,
+  output reg [31:0] wbs_dat_o,
+
+  input ring_clk,
+  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
+);
+  // Register addresses.
+  localparam COUNT_VALUE_ADDR = 8'h00;
+  localparam CONTROL_ADDR     = 8'h04;
+  localparam TRIMA_ADDR       = 8'h08;
+  localparam TRIMB_ADDR       = 8'h0c;
+
+  wire resetb;
+  assign resetb = ~wb_rst_i;
+
+  wire slave_sel = (wb_stb_i && wb_cyc_i);
+  wire slave_write_en = (|wb_sel_i && wb_we_i);
+
+  wire control_sel = |(wb_adr_i[7:0] & CONTROL_ADDR);
+  wire count_value_sel = |(wb_adr_i[7:0] & COUNT_VALUE_ADDR);
+  wire trima_sel = |(wb_adr_i[7:0] & TRIMA_ADDR);
+  wire trimb_sel = |(wb_adr_i[7:0] & TRIMB_ADDR);
+
+  // Counter signals and regs.
+  reg counter_resetb;
+  reg [16:0] counter_value;
+
+  // 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.
+  always @(posedge ring_clk or negedge counter_resetb) begin
+    if (!counter_resetb) begin
+      counter_value <= 16'h0;
+    end else begin
+      counter_value <= counter_value + 16'h1;
+    end
+  end
+
+  // CSRs.
+  always @(posedge wb_clk_i or negedge resetb) begin
+    if (!resetb) begin
+      wb_ack_o <= 1'b0;
+      wb_dat_o <= 32'h0;
+      counter_resetb <= 1'b0;
+      ring_start <= 1'b0;
+      ring_clkmux <= {CLKMUX_BITS{1'b1}};
+      ring_trim_a <= {TRIM_BITS{1b'1}};
+      ring_trim_b <= {TRIM_BITS{1b'1}};
+    end else begin
+      wb_ack_o <= 1'b0;
+
+      if (slave_sel && !wb_ack_o) begin
+        wb_ack_o <= 1'b1;
+
+        if (control_sel) begin
+          counter_resetb <= wb_dat_i[0];
+          ring_start <= wb_dat_i[1];
+          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];
+        end else if (trimb_sel) begin
+          ring_trim_b <= wb_dat_i[TRIM_BITS-1:0];
+        end else if (count_value_sel) begin
+          // TODO(hdpham): Add synchronizer!
+          wb_dat_o <= {16'h0, counter_value};
+        end
+      end
+    end
+  end
+
+endmodule // module ring_control