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