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