Add PWM
diff --git a/ip/randsack/rtl/digitalcore_macro.v b/ip/randsack/rtl/digitalcore_macro.v
index 4f134bc..467fbc5 100644
--- a/ip/randsack/rtl/digitalcore_macro.v
+++ b/ip/randsack/rtl/digitalcore_macro.v
@@ -67,14 +67,20 @@
// Peripherals.
parameter GPIO0_ADDR_MASK = 32'hffff_0000;
parameter GPIO0_BASE_ADDR = 32'h3080_0000;
- parameter PWM0_ADDR_MASK = 32'hffff_0000;
+ parameter PWM0_ADDR_MASK = 32'hffff_ff00;
parameter PWM0_BASE_ADDR = 32'h3081_0000;
+ parameter PWM1_ADDR_MASK = 32'hffff_ff00;
+ parameter PWM1_BASE_ADDR = 32'h3081_0100;
+ parameter PWM2_ADDR_MASK = 32'hffff_ff00;
+ parameter PWM2_BASE_ADDR = 32'h3081_0200;
+ parameter PWM3_ADDR_MASK = 32'hffff_ff00;
+ parameter PWM3_BASE_ADDR = 32'h3081_0300;
parameter UART0_ADDR_MASK = 32'hffff_0000;
parameter UART0_BASE_ADDR = 32'h3082_0000;
- parameter RING0_ADDR_MASK = 32'hffff_0000;
+ parameter RING0_ADDR_MASK = 32'hffff_ff00;
parameter RING0_BASE_ADDR = 32'h3083_0000;
- parameter RING1_ADDR_MASK = 32'hffff_0000;
- parameter RING1_BASE_ADDR = 32'h3084_0000;
+ parameter RING1_ADDR_MASK = 32'hffff_ff00;
+ parameter RING1_BASE_ADDR = 32'h3083_0100;
// 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
@@ -83,7 +89,7 @@
wire wbs_addr_sel;
assign wbs_addr_sel = (wbs_adr_i & DTOP_MASK) == DTOP_ADDR;
- wb_mux_4 interconnect (
+ wb_mux_8 interconnect (
.wbm_adr_i(wbs_adr_i),
.wbm_dat_i(wbs_dat_i),
.wbm_dat_o(wbs_dat_o),
@@ -108,44 +114,96 @@
.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_adr_o(pwm_adr_i[0]),
+ .wbs1_dat_i(pwm_dat_o[0]),
+ .wbs1_dat_o(pwm_dat_i[0]),
+ .wbs1_we_o(pwm_we_i[0]),
+ .wbs1_sel_o(pwm_sel_i[0]),
+ .wbs1_stb_o(pwm_stb_i[0]),
+ .wbs1_ack_i(pwm_ack_o[0]),
.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),
+ .wbs1_cyc_o(pwm_cyc_i[0]),
+ .wbs1_addr(PWM0_BASE_ADDR),
+ .wbs1_addr_msk(PWM0_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_adr_o(pwm_adr_i[1]),
+ .wbs2_dat_i(pwm_dat_o[1]),
+ .wbs2_dat_o(pwm_dat_i[1]),
+ .wbs2_we_o(pwm_we_i[1]),
+ .wbs2_sel_o(pwm_sel_i[1]),
+ .wbs2_stb_o(pwm_stb_i[1]),
+ .wbs2_ack_i(pwm_ack_o[1]),
.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),
+ .wbs2_cyc_o(pwm_cyc_i[1]),
+ .wbs2_addr(PWM1_BASE_ADDR),
+ .wbs2_addr_msk(PWM1_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_adr_o(pwm_adr_i[2]),
+ .wbs3_dat_i(pwm_dat_o[2]),
+ .wbs3_dat_o(pwm_dat_i[2]),
+ .wbs3_we_o(pwm_we_i[2]),
+ .wbs3_sel_o(pwm_sel_i[2]),
+ .wbs3_stb_o(pwm_stb_i[2]),
+ .wbs3_ack_i(pwm_ack_o[2]),
.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)
+ .wbs3_cyc_o(pwm_cyc_i[2]),
+ .wbs3_addr(PWM2_BASE_ADDR),
+ .wbs3_addr_msk(PWM2_ADDR_MASK),
+
+ .wbs4_adr_o(pwm_adr_i[3]),
+ .wbs4_dat_i(pwm_dat_o[3]),
+ .wbs4_dat_o(pwm_dat_i[3]),
+ .wbs4_we_o(pwm_we_i[3]),
+ .wbs4_sel_o(pwm_sel_i[3]),
+ .wbs4_stb_o(pwm_stb_i[3]),
+ .wbs4_ack_i(pwm_ack_o[3]),
+ .wbs4_err_i(1'b0),
+ .wbs4_rty_i(1'b0),
+ .wbs4_cyc_o(pwm_cyc_i[3]),
+ .wbs4_addr(PWM3_BASE_ADDR),
+ .wbs4_addr_msk(PWM3_ADDR_MASK),
+
+ .wbs5_adr_o(uart_adr_i),
+ .wbs5_dat_i(uart_dat_o),
+ .wbs5_dat_o(uart_dat_i),
+ .wbs5_we_o(uart_we_i),
+ .wbs5_sel_o(uart_sel_i),
+ .wbs5_stb_o(uart_stb_i),
+ .wbs5_ack_i(uart_ack_o),
+ .wbs5_err_i(1'b0),
+ .wbs5_rty_i(1'b0),
+ .wbs5_cyc_o(uart_cyc_i),
+ .wbs5_addr(UART0_BASE_ADDR),
+ .wbs5_addr_msk(UART0_ADDR_MASK),
+
+ .wbs6_adr_o(ring0_adr_i),
+ .wbs6_dat_i(ring0_dat_o),
+ .wbs6_dat_o(ring0_dat_i),
+ .wbs6_we_o(ring0_we_i),
+ .wbs6_sel_o(ring0_sel_i),
+ .wbs6_stb_o(ring0_stb_i),
+ .wbs6_ack_i(ring0_ack_o),
+ .wbs6_err_i(1'b0),
+ .wbs6_rty_i(1'b0),
+ .wbs6_cyc_o(ring0_cyc_i),
+ .wbs6_addr(RING0_BASE_ADDR),
+ .wbs6_addr_msk(RING0_ADDR_MASK),
+
+ .wbs7_adr_o(ring1_adr_i),
+ .wbs7_dat_i(ring1_dat_o),
+ .wbs7_dat_o(ring1_dat_i),
+ .wbs7_we_o(ring1_we_i),
+ .wbs7_sel_o(ring1_sel_i),
+ .wbs7_stb_o(ring1_stb_i),
+ .wbs7_ack_i(ring1_ack_o),
+ .wbs7_err_i(1'b0),
+ .wbs7_rty_i(1'b0),
+ .wbs7_cyc_o(ring1_cyc_i),
+ .wbs7_addr(RING1_BASE_ADDR),
+ .wbs7_addr_msk(RING1_ADDR_MASK)
);
// GPIO signals.
@@ -181,6 +239,40 @@
.gpio_oeb(gpio_oeb)
);
+ // PWM controllers.
+ wire [31:0] pwm_adr_i[4];
+ wire [31:0] pwm_dat_i[4];
+ wire [3:0] pwm_sel_i[4];
+ wire pwm_we_i[4];
+ wire pwm_cyc_i[4];
+ wire pwm_stb_i[4];
+ wire pwm_ack_o[4];
+ wire [31:0] pwm_dat_o[4];
+
+ wire pwm_out[4];
+
+ genvar i;
+ generate
+ for (i = 0; i < 4; i = i + 1) begin : pwms
+ pwm_wb pwm (
+ .wb_clk_i(wb_clk_i),
+ .wb_rst_i(wb_rst_i),
+
+ .wb_stb_i(pwm_stb_i[i]),
+ .wb_cyc_i(pwm_cyc_i[i]),
+ .wb_we_i(pwm_we_i[i]),
+ .wb_sel_i(pwm_sel_i[i]),
+ .wb_dat_i(pwm_dat_i[i]),
+ .wb_adr_i(pwm_adr_i[i]),
+
+ .wb_ack_o(pwm_ack_o[i]),
+ .wb_dat_o(pwm_dat_o[i]),
+
+ .pwm_out(pwm_out[i])
+ );
+ end
+ endgenerate
+
// UART signals.
wire [31:0] uart_adr_i;
wire [31:0] uart_dat_i;
@@ -301,18 +393,32 @@
assign io_out[5:0] = 6'b0;
assign io_out[37:6] = gpio_out |
{
- /*io37=*/uart_tx & uart_enabled,
- 4'b0,
- /*io32=*/ring0_test_out,
- /*io31=*/ring1_test_out,
- 25'b0
+ /*31 - io37=*/uart_tx & uart_enabled,
+ /*30 - io36=*/1'b0,
+ /*29 - io35=*/1'b0,
+ /*28 - io34=*/1'b0,
+ /*27 - io33=*/1'b0,
+ /*26 - io32=*/ring0_test_out,
+ /*25 - io31=*/ring1_test_out,
+ /*24 - io30=*/1'b0,
+ /*23 - io29=*/pwm_out[0],
+ /*22 - io28=*/1'b0,
+ /*21 - io27=*/pwm_out[1],
+ /*20 - io26=*/1'b0,
+ /*19 - io25=*/pwm_out[2],
+ /*18 - io24=*/1'b0,
+ /*17 - io23=*/pwm_out[3],
+ 17'b0
};
assign io_oeb[5:0] = 6'b0;
assign io_oeb[37:6] = gpio_oeb &
{
/*io37=*/~uart_enabled,
- {4{1'b1}},
+ /*io36=*/1'b1,
+ /*io35=*/1'b1,
+ /*io34=*/1'b1,
+ /*io33=*/1'b1,
/*io32=*/~ring0_test_en,
/*io31=*/~ring1_test_en,
{25{1'b1}}
diff --git a/ip/randsack/rtl/pwm_wb.v b/ip/randsack/rtl/pwm_wb.v
new file mode 100644
index 0000000..58bc4a7
--- /dev/null
+++ b/ip/randsack/rtl/pwm_wb.v
@@ -0,0 +1,131 @@
+// PWM generator.
+//
+// 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 pwm_wb #(
+ parameter DIV_BITS = 16,
+ parameter CNT_BITS = 16
+) (
+ input wb_clk_i,
+ input wb_rst_i,
+
+ input wb_stb_i,
+ input wb_cyc_i,
+ input wb_we_i,
+ input [3:0] wb_sel_i,
+ input [31:0] wb_dat_i,
+ input [31:0] wb_adr_i,
+
+ output reg wb_ack_o,
+ output reg [31:0] wb_dat_o,
+
+ output reg pwm_out
+);
+ // CSR addresses.
+ localparam DIV_ADDR = 8'h00;
+ localparam CNTMAX_ADDR = 8'h04;
+ localparam CNT_ADDR = 8'h08;
+ localparam CMP_ADDR = 8'h0c;
+
+ wire clk = wb_clk_i;
+ wire resetb = ~wb_rst_i;
+
+ wire slave_sel = (wb_stb_i && wb_cyc_i);
+ wire slave_write_en = (|wb_sel_i && wb_we_i);
+
+ wire div_sel = (wb_adr_i[7:0] == DIV_ADDR);
+ wire cntmax_sel = (wb_adr_i[7:0] == CNTMAX_ADDR);
+ wire cnt_sel = (wb_adr_i[7:0] == CNT_ADDR);
+ wire cmp_sel = (wb_adr_i[7:0] == CMP_ADDR);
+
+ // CSR regs.
+ reg [DIV_BITS-1:0] div_reg;
+ reg [CNT_BITS-1:0] cntmax_reg;
+ reg [CNT_BITS-1:0] cmp_reg;
+
+ // Counters.
+ reg [DIV_BITS-1:0] div_cnt;
+ reg [CNT_BITS-1:0] cnt_cnt;
+
+ // CSRs.
+ always @(posedge clk or negedge resetb) begin
+ if (!resetb) begin
+ div_reg <= {DIV_BITS{1'b0}};
+ cntmax_reg <= {CNT_BITS{1'b0}};
+ cmp_reg <= {CNT_BITS{1'b0}};
+ end else begin
+ wb_ack_o <= 1'b0;
+ if (slave_sel && !wb_ack_o && slave_write_en) begin
+ wb_ack_o <= 1'b1;
+ if (div_sel) begin
+ div_reg <= wb_dat_i[DIV_BITS-1:0];
+ end else if (cntmax_sel) begin
+ cntmax_reg <= wb_dat_i[CNT_BITS-1:0];
+ end else if (cmp_sel) begin
+ cmp_reg <= wb_dat_i[CNT_BITS-1:0];
+ end
+ end
+ if (slave_sel && !wb_ack_o && !slave_write_en) begin
+ wb_ack_o <= 1'b1;
+ if (cnt_sel) begin
+ wb_dat_o <= cnt_cnt;
+ end
+ end
+ end
+ end
+
+ // DIV counter.
+ wire div_match = div_cnt == div_reg;
+ always @(posedge clk or negedge resetb) begin
+ if (!resetb) begin
+ div_cnt <= {DIV_BITS{1'b0}};
+ end else begin
+ if (div_match) begin
+ div_cnt <= {DIV_BITS{1'b0}};
+ end else begin
+ div_cnt <= div_cnt + 1'b1;
+ end
+ end
+ end
+
+ // CNT counter.
+ always @(posedge clk or negedge resetb) begin
+ if (!resetb) begin
+ cnt_cnt <= {CNT_BITS{1'b0}};
+ end else begin
+ if (!div_match) begin
+ end else if (cnt_cnt == cntmax_reg) begin
+ cnt_cnt <= {CNT_BITS{1'b0}};
+ end else begin
+ cnt_cnt <= cnt_cnt + 1'b1;
+ end
+ end
+ end
+
+ // Compare.
+ always @(posedge clk or negedge resetb) begin
+ if (!resetb) begin
+ pwm_out <= 1'b0;
+ end else begin
+ if (cnt_cnt < cmp_reg) begin
+ pwm_out <= 1'b1;
+ end else begin
+ pwm_out <= 1'b0;
+ end
+ end
+ end
+
+endmodule // module pwm_wb