diff --git a/rtl.flist b/rtl.flist index 834d9c1..fac319d 100644 --- a/rtl.flist +++ b/rtl.flist @@ -33,7 +33,6 @@ ../rtl/debug/jtag_top.sv ../rtl/debug/debug_rom.sv -../rtl/perips/gpio.sv ../rtl/perips/ram.sv ../rtl/perips/rom.sv ../rtl/perips/rvic.sv @@ -47,6 +46,10 @@ ../rtl/perips/timer/timer_reg_top.sv ../rtl/perips/timer/timer_core.sv ../rtl/perips/timer/timer_top.sv +../rtl/perips/gpio/gpio_reg_pkg.sv +../rtl/perips/gpio/gpio_reg_top.sv +../rtl/perips/gpio/gpio_core.sv +../rtl/perips/gpio/gpio_top.sv ../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv @@ -64,3 +67,4 @@ ../rtl/utils/prim_subreg.sv ../rtl/utils/prim_subreg_arb.sv ../rtl/utils/prim_subreg_ext.sv +../rtl/utils/prim_filter.sv diff --git a/rtl/perips/gpio.sv b/rtl/perips/gpio.sv deleted file mode 100644 index f0ca77b..0000000 --- a/rtl/perips/gpio.sv +++ /dev/null @@ -1,120 +0,0 @@ - /* - Copyright 2020 Blue Liang, liangkangnan@163.com - - 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. - */ - -// GPIO模块 -module gpio( - - input wire clk, - input wire rst_n, - input wire[31:0] addr_i, - input wire[31:0] data_i, - input wire[3:0] sel_i, - input wire we_i, - output wire[31:0] data_o, - - input wire[1:0] io_pin_i, - output wire[31:0] reg_ctrl, - output wire[31:0] reg_data - - ); - - // GPIO寄存器(偏移)地址 - localparam GPIO_CTRL = 4'h0; - localparam GPIO_DATA = 4'h4; - - // GPIO控制寄存器 - // 每2位控制1个IO的输入、输出模式,最多支持16个IO - // 0: 高阻,1:输出,2:输入 - reg[31:0] gpio_ctrl; - - // GPIO输入输出数据寄存器 - reg[31:0] gpio_data; - - assign reg_ctrl = gpio_ctrl; - assign reg_data = gpio_data; - - wire wen = we_i; - wire ren = (~we_i); - wire write_reg_ctrl_en = wen & (addr_i[3:0] == GPIO_CTRL); - wire write_reg_data_en = wen & (addr_i[3:0] == GPIO_DATA); - - // 写gpio_ctrl - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - gpio_ctrl <= 32'h0; - end else begin - if (write_reg_ctrl_en) begin - if (sel_i[0]) begin - gpio_ctrl[7:0] <= data_i[7:0]; - end - if (sel_i[1]) begin - gpio_ctrl[15:8] <= data_i[15:8]; - end - if (sel_i[2]) begin - gpio_ctrl[23:16] <= data_i[23:16]; - end - if (sel_i[3]) begin - gpio_ctrl[31:24] <= data_i[31:24]; - end - end - end - end - - // 写gpio_data - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - gpio_data <= 32'h0; - end else begin - if (write_reg_data_en) begin - if (sel_i[0]) begin - gpio_data[7:0] <= data_i[7:0]; - end - if (sel_i[1]) begin - gpio_data[15:8] <= data_i[15:8]; - end - end else begin - if (gpio_ctrl[1:0] == 2'b10) begin - gpio_data[0] <= io_pin_i[0]; - end - if (gpio_ctrl[3:2] == 2'b10) begin - gpio_data[1] <= io_pin_i[1]; - end - end - end - end - - reg[31:0] data_r; - - // 读寄存器 - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - data_r <= 32'h0; - end else begin - if (ren) begin - case (addr_i[3:0]) - GPIO_CTRL: data_r <= gpio_ctrl; - GPIO_DATA: data_r <= gpio_data; - default: data_r <= 32'h0; - endcase - end else begin - data_r <= 32'h0; - end - end - end - - assign data_o = data_r; - -endmodule diff --git a/rtl/perips/gpio/gpio.h b/rtl/perips/gpio/gpio.h new file mode 100644 index 0000000..f50cbc0 --- /dev/null +++ b/rtl/perips/gpio/gpio.h @@ -0,0 +1,59 @@ +// Generated register defines for gpio + +// Copyright information found in source file: +// Copyright lowRISC contributors. + +// Licensing information found in source file: +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef _GPIO_REG_DEFS_ +#define _GPIO_REG_DEFS_ + +#ifdef __cplusplus +extern "C" { +#endif +// Register width +#define GPIO_PARAM_REG_WIDTH 32 + +// gpio mode register +#define GPIO_MODE_REG_OFFSET 0x0 +#define GPIO_MODE_REG_RESVAL 0x0 +#define GPIO_MODE_GPIO_MASK 0xffff +#define GPIO_MODE_GPIO_OFFSET 0 +#define GPIO_MODE_GPIO_FIELD \ + ((bitfield_field32_t) { .mask = GPIO_MODE_GPIO_MASK, .index = GPIO_MODE_GPIO_OFFSET }) + +// gpio interrupt register +#define GPIO_INTR_REG_OFFSET 0x4 +#define GPIO_INTR_REG_RESVAL 0x0 +#define GPIO_INTR_GPIO_INT_MASK 0xffff +#define GPIO_INTR_GPIO_INT_OFFSET 0 +#define GPIO_INTR_GPIO_INT_FIELD \ + ((bitfield_field32_t) { .mask = GPIO_INTR_GPIO_INT_MASK, .index = GPIO_INTR_GPIO_INT_OFFSET }) +#define GPIO_INTR_GPIO_PENDING_MASK 0xff +#define GPIO_INTR_GPIO_PENDING_OFFSET 16 +#define GPIO_INTR_GPIO_PENDING_FIELD \ + ((bitfield_field32_t) { .mask = GPIO_INTR_GPIO_PENDING_MASK, .index = GPIO_INTR_GPIO_PENDING_OFFSET }) + +// gpio data register +#define GPIO_DATA_REG_OFFSET 0x8 +#define GPIO_DATA_REG_RESVAL 0x0 +#define GPIO_DATA_GPIO_MASK 0xff +#define GPIO_DATA_GPIO_OFFSET 0 +#define GPIO_DATA_GPIO_FIELD \ + ((bitfield_field32_t) { .mask = GPIO_DATA_GPIO_MASK, .index = GPIO_DATA_GPIO_OFFSET }) + +// gpio input filter enable register +#define GPIO_FILTER_REG_OFFSET 0xc +#define GPIO_FILTER_REG_RESVAL 0x0 +#define GPIO_FILTER_GPIO_MASK 0xff +#define GPIO_FILTER_GPIO_OFFSET 0 +#define GPIO_FILTER_GPIO_FIELD \ + ((bitfield_field32_t) { .mask = GPIO_FILTER_GPIO_MASK, .index = GPIO_FILTER_GPIO_OFFSET }) + +#ifdef __cplusplus +} // extern "C" +#endif +#endif // _GPIO_REG_DEFS_ +// End generated register defines for gpio \ No newline at end of file diff --git a/rtl/perips/gpio/gpio.hjson b/rtl/perips/gpio/gpio.hjson new file mode 100644 index 0000000..5b6e947 --- /dev/null +++ b/rtl/perips/gpio/gpio.hjson @@ -0,0 +1,61 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ name: "gpio", + clocking: [{clock: "clk_i", reset: "rst_ni"}], + bus_interfaces: [ + { protocol: "tlul", direction: "device" } + ], + regwidth: "32", + registers: [ + { name: "MODE", + desc: "gpio mode register", + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "15:0", + name: "GPIO", + desc: "gpio input or output mode, 2 bits for each gpio", + } + ] + } + { name: "INTR", + desc: "gpio interrupt register", + swaccess: "rw", + hwaccess: "hrw", + fields: [ + { bits: "15:0", + name: "GPIO_INT", + desc: "gpio interrupt mode, 2 bits for each gpio", + } + { bits: "23:16", + name: "GPIO_PENDING", + swaccess: "rw1c", + desc: "gpio interrupt pending, 1 bits for each gpio", + } + ] + } + { name: "DATA", + desc: "gpio data register", + swaccess: "rw", + hwaccess: "hrw", + fields: [ + { bits: "7:0", + name: "GPIO", + desc: "gpio input or output data, 1 bits for each gpio", + } + ] + } + { name: "FILTER", + desc: "gpio input filter enable register", + swaccess: "rw", + hwaccess: "hro", + fields: [ + { bits: "7:0", + name: "GPIO", + desc: "gpio input filter enable, 1 bits for each gpio", + } + ] + } + ] +} diff --git a/rtl/perips/gpio/gpio_core.sv b/rtl/perips/gpio/gpio_core.sv new file mode 100644 index 0000000..9f04985 --- /dev/null +++ b/rtl/perips/gpio/gpio_core.sv @@ -0,0 +1,158 @@ + /* + Copyright 2021 Blue Liang, liangkangnan@163.com + + 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. + */ + +// 目前最多支持8个GPIO +module gpio_core #( + parameter int GPIO_NUM = 2 + )( + input logic clk_i, + input logic rst_ni, + + output logic [GPIO_NUM-1:0] gpio_oe_o, + output logic [GPIO_NUM-1:0] gpio_data_o, + input logic [GPIO_NUM-1:0] gpio_data_i, + output logic irq_gpio0_o, + output logic irq_gpio1_o, + output logic irq_gpio2_4_o, + output logic irq_gpio5_7_o, + + input logic reg_we_i, + input logic reg_re_i, + input logic [31:0] reg_wdata_i, + input logic [ 3:0] reg_be_i, + input logic [31:0] reg_addr_i, + output logic [31:0] reg_rdata_o + ); + + localparam logic [1:0] INTR_MODE_RAISE_EDGE = 2'd1; + localparam logic [1:0] INTR_MODE_FALL_EDGE = 2'd2; + localparam logic [1:0] INTR_MODE_DOUBLE_EDGE = 2'd3; + + localparam logic [1:0] GPIO_MODE_INPUT = 2'd1; + localparam logic [1:0] GPIO_MODE_OUTPUT = 2'd2; + + import gpio_reg_pkg::*; + + gpio_reg_pkg::gpio_reg2hw_t reg2hw; + gpio_reg_pkg::gpio_hw2reg_t hw2reg; + + logic [GPIO_NUM-1:0] gpio_oe; + logic [GPIO_NUM-1:0] gpio_ie; + logic [GPIO_NUM-1:0] gpio_data; + logic [GPIO_NUM-1:0] gpio_filter_enable; + logic [GPIO_NUM-1:0] gpio_filter_data; + logic [GPIO_NUM-1:0] gpio_raise_detect; + logic [GPIO_NUM-1:0] gpio_fall_detect; + logic [GPIO_NUM-1:0] gpio_intr_trigge; + + // 输入滤波使能 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_filter_enable + assign gpio_filter_enable[i] = reg2hw.filter.q[i]; + end + + // 输出使能 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_oe + assign gpio_oe[i] = reg2hw.mode.q[i*2+1:i*2] == GPIO_MODE_OUTPUT; + end + + assign gpio_oe_o = gpio_oe; + + // 输出数据 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_data + assign gpio_data[i] = reg2hw.data.q[i]; + end + + assign gpio_data_o = gpio_data; + + // 输入使能 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_ie + assign gpio_ie[i] = reg2hw.mode.q[i*2+1:i*2] == GPIO_MODE_INPUT; + end + + // 硬件写data数据 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_h2r_data + assign hw2reg.data.d[i] = gpio_ie[i] ? gpio_filter_data[i] : reg2hw.data.q[i]; + end + // 硬件写data使能 + assign hw2reg.data.de = |gpio_ie; + + // 中断有效 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_intr_trigge + assign gpio_intr_trigge[i] = ((reg2hw.intr.gpio_int[i*2+1:i*2] == INTR_MODE_RAISE_EDGE) & gpio_raise_detect[i]) | + ((reg2hw.intr.gpio_int[i*2+1:i*2] == INTR_MODE_FALL_EDGE) & gpio_fall_detect[i]) | + ((reg2hw.intr.gpio_int[i*2+1:i*2] == INTR_MODE_DOUBLE_EDGE) & (gpio_raise_detect[i] | gpio_fall_detect[i])); + end + + // 硬件写中断pending数据 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_intr_pending + assign hw2reg.intr.gpio_pending.d[i] = gpio_intr_trigge[i] ? 1'b1 : reg2hw.intr.gpio_pending.q[i]; + end + // 硬件写中断pending使能 + assign hw2reg.intr.gpio_pending.de = |gpio_intr_trigge; + + // 中断输出信号 + if (GPIO_NUM >= 1) begin : g_num_ge_1 + assign irq_gpio0_o = reg2hw.intr.gpio_pending.q[0]; + end + if (GPIO_NUM >= 2) begin : g_num_ge_2 + assign irq_gpio1_o = reg2hw.intr.gpio_pending.q[1]; + end + if (GPIO_NUM >= 5) begin : g_num_ge_5 + assign irq_gpio2_4_o = reg2hw.intr.gpio_pending.q[2] | reg2hw.intr.gpio_pending.q[3] | reg2hw.intr.gpio_pending.q[4]; + end + if (GPIO_NUM >= 8) begin : g_num_ge_8 + assign irq_gpio5_7_o = reg2hw.intr.gpio_pending.q[5] | reg2hw.intr.gpio_pending.q[6] | reg2hw.intr.gpio_pending.q[7]; + end + + // 沿检测 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_edge_detect + edge_detect u_edge_detect( + .clk_i (clk_i), + .rst_ni (rst_ni), + .sig_i (gpio_filter_data[i]), + .sig_o (), + .re_o (gpio_raise_detect[i]), + .fe_o (gpio_fall_detect[i]) + ); + end + + // 输入信号滤波 + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_filter + prim_filter #( + .Cycles(8) + ) gpio_filter ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .enable_i (gpio_filter_enable[i]), + .filter_i (gpio_data_i[i]), + .filter_o (gpio_filter_data[i]) + ); + end + + gpio_reg_top u_gpio_reg_top ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .reg2hw (reg2hw), + .hw2reg (hw2reg), + .reg_we (reg_we_i), + .reg_re (reg_re_i), + .reg_wdata (reg_wdata_i), + .reg_be (reg_be_i), + .reg_addr (reg_addr_i), + .reg_rdata (reg_rdata_o) + ); + +endmodule diff --git a/rtl/perips/gpio/gpio_reg_pkg.sv b/rtl/perips/gpio/gpio_reg_pkg.sv new file mode 100644 index 0000000..ea56835 --- /dev/null +++ b/rtl/perips/gpio/gpio_reg_pkg.sv @@ -0,0 +1,90 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package gpio_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 4; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + logic [15:0] q; + } gpio_reg2hw_mode_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] q; + } gpio_int; + struct packed { + logic [7:0] q; + } gpio_pending; + } gpio_reg2hw_intr_reg_t; + + typedef struct packed { + logic [7:0] q; + } gpio_reg2hw_data_reg_t; + + typedef struct packed { + logic [7:0] q; + } gpio_reg2hw_filter_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] d; + logic de; + } gpio_int; + struct packed { + logic [7:0] d; + logic de; + } gpio_pending; + } gpio_hw2reg_intr_reg_t; + + typedef struct packed { + logic [7:0] d; + logic de; + } gpio_hw2reg_data_reg_t; + + // Register -> HW type + typedef struct packed { + gpio_reg2hw_mode_reg_t mode; // [55:40] + gpio_reg2hw_intr_reg_t intr; // [39:16] + gpio_reg2hw_data_reg_t data; // [15:8] + gpio_reg2hw_filter_reg_t filter; // [7:0] + } gpio_reg2hw_t; + + // HW -> register type + typedef struct packed { + gpio_hw2reg_intr_reg_t intr; // [34:9] + gpio_hw2reg_data_reg_t data; // [8:0] + } gpio_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] GPIO_MODE_OFFSET = 4'h0; + parameter logic [BlockAw-1:0] GPIO_INTR_OFFSET = 4'h4; + parameter logic [BlockAw-1:0] GPIO_DATA_OFFSET = 4'h8; + parameter logic [BlockAw-1:0] GPIO_FILTER_OFFSET = 4'hc; + + // Register index + typedef enum int { + GPIO_MODE, + GPIO_INTR, + GPIO_DATA, + GPIO_FILTER + } gpio_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] GPIO_PERMIT [4] = '{ + 4'b0011, // index[0] GPIO_MODE + 4'b0111, // index[1] GPIO_INTR + 4'b0001, // index[2] GPIO_DATA + 4'b0001 // index[3] GPIO_FILTER + }; + +endpackage + diff --git a/rtl/perips/gpio/gpio_reg_top.sv b/rtl/perips/gpio/gpio_reg_top.sv new file mode 100644 index 0000000..b385fcc --- /dev/null +++ b/rtl/perips/gpio/gpio_reg_top.sv @@ -0,0 +1,263 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +module gpio_reg_top ( + input logic clk_i, + input logic rst_ni, + + // To HW + output gpio_reg_pkg::gpio_reg2hw_t reg2hw, // Write + input gpio_reg_pkg::gpio_hw2reg_t hw2reg, // Read + + input logic reg_we, + input logic reg_re, + input logic [31:0] reg_wdata, + input logic [ 3:0] reg_be, + input logic [31:0] reg_addr, + output logic [31:0] reg_rdata +); + + import gpio_reg_pkg::* ; + + localparam int AW = 4; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + logic reg_error; + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + assign reg_rdata = reg_rdata_next; + assign reg_error = wr_err; + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic mode_we; + logic [15:0] mode_qs; + logic [15:0] mode_wd; + logic intr_we; + logic [15:0] intr_gpio_int_qs; + logic [15:0] intr_gpio_int_wd; + logic [7:0] intr_gpio_pending_qs; + logic [7:0] intr_gpio_pending_wd; + logic data_we; + logic [7:0] data_qs; + logic [7:0] data_wd; + logic filter_we; + logic [7:0] filter_qs; + logic [7:0] filter_wd; + + // Register instances + // R[mode]: V(False) + + prim_subreg #( + .DW (16), + .SWACCESS("RW"), + .RESVAL (16'h0) + ) u_mode ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (mode_we), + .wd (mode_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.mode.q), + + // to register interface (read) + .qs (mode_qs) + ); + + + // R[intr]: V(False) + + // F[gpio_int]: 15:0 + prim_subreg #( + .DW (16), + .SWACCESS("RW"), + .RESVAL (16'h0) + ) u_intr_gpio_int ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_we), + .wd (intr_gpio_int_wd), + + // from internal hardware + .de (hw2reg.intr.gpio_int.de), + .d (hw2reg.intr.gpio_int.d), + + // to internal hardware + .qe (), + .q (reg2hw.intr.gpio_int.q), + + // to register interface (read) + .qs (intr_gpio_int_qs) + ); + + + // F[gpio_pending]: 23:16 + prim_subreg #( + .DW (8), + .SWACCESS("W1C"), + .RESVAL (8'h0) + ) u_intr_gpio_pending ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (intr_we), + .wd (intr_gpio_pending_wd), + + // from internal hardware + .de (hw2reg.intr.gpio_pending.de), + .d (hw2reg.intr.gpio_pending.d), + + // to internal hardware + .qe (), + .q (reg2hw.intr.gpio_pending.q), + + // to register interface (read) + .qs (intr_gpio_pending_qs) + ); + + + // R[data]: V(False) + + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'h0) + ) u_data ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (data_we), + .wd (data_wd), + + // from internal hardware + .de (hw2reg.data.de), + .d (hw2reg.data.d), + + // to internal hardware + .qe (), + .q (reg2hw.data.q), + + // to register interface (read) + .qs (data_qs) + ); + + + // R[filter]: V(False) + + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'h0) + ) u_filter ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (filter_we), + .wd (filter_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (), + .q (reg2hw.filter.q), + + // to register interface (read) + .qs (filter_qs) + ); + + + logic [3:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == GPIO_MODE_OFFSET); + addr_hit[1] = (reg_addr == GPIO_INTR_OFFSET); + addr_hit[2] = (reg_addr == GPIO_DATA_OFFSET); + addr_hit[3] = (reg_addr == GPIO_FILTER_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(GPIO_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(GPIO_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(GPIO_PERMIT[2] & ~reg_be))) | + (addr_hit[3] & (|(GPIO_PERMIT[3] & ~reg_be))))); + end + + assign mode_we = addr_hit[0] & reg_we & !reg_error; + + assign mode_wd = reg_wdata[15:0]; + assign intr_we = addr_hit[1] & reg_we & !reg_error; + + assign intr_gpio_int_wd = reg_wdata[15:0]; + + assign intr_gpio_pending_wd = reg_wdata[23:16]; + assign data_we = addr_hit[2] & reg_we & !reg_error; + + assign data_wd = reg_wdata[7:0]; + assign filter_we = addr_hit[3] & reg_we & !reg_error; + + assign filter_wd = reg_wdata[7:0]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[15:0] = mode_qs; + end + + addr_hit[1]: begin + reg_rdata_next[15:0] = intr_gpio_int_qs; + reg_rdata_next[23:16] = intr_gpio_pending_qs; + end + + addr_hit[2]: begin + reg_rdata_next[7:0] = data_qs; + end + + addr_hit[3]: begin + reg_rdata_next[7:0] = filter_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + +endmodule diff --git a/rtl/perips/gpio/gpio_top.sv b/rtl/perips/gpio/gpio_top.sv new file mode 100644 index 0000000..cb81fd1 --- /dev/null +++ b/rtl/perips/gpio/gpio_top.sv @@ -0,0 +1,76 @@ + /* + Copyright 2021 Blue Liang, liangkangnan@163.com + + 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 gpio_top #( + parameter int GPIO_NUM = 2 + )( + input logic clk_i, + input logic rst_ni, + + output logic [GPIO_NUM-1:0] gpio_oe_o, + output logic [GPIO_NUM-1:0] gpio_data_o, + input logic [GPIO_NUM-1:0] gpio_data_i, + output logic irq_gpio0_o, + output logic irq_gpio1_o, + output logic irq_gpio2_4_o, + output logic irq_gpio5_7_o, + + // OBI总线接口信号 + input logic req_i, + input logic we_i, + input logic [ 3:0] be_i, + input logic [31:0] addr_i, + input logic [31:0] data_i, + output logic [31:0] data_o + ); + + logic re; + logic we; + logic [31:0] addr; + logic [31:0] reg_rdata; + + // 读信号 + assign re = req_i & (!we_i); + // 写信号 + assign we = req_i & we_i; + // 去掉基地址 + assign addr = {16'h0, addr_i[15:0]}; + + gpio_core #( + .GPIO_NUM(GPIO_NUM) + ) u_gpio_core ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .gpio_oe_o (gpio_oe_o), + .gpio_data_o(gpio_data_o), + .gpio_data_i(gpio_data_i), + .irq_gpio0_o(irq_gpio0_o), + .irq_gpio1_o(irq_gpio1_o), + .irq_gpio2_4_o(irq_gpio2_4_o), + .irq_gpio5_7_o(irq_gpio5_7_o), + .reg_we_i (we), + .reg_re_i (re), + .reg_wdata_i(data_i), + .reg_be_i (be_i), + .reg_addr_i (addr), + .reg_rdata_o(reg_rdata) + ); + + always_ff @(posedge clk_i) begin + data_o <= reg_rdata; + end + +endmodule diff --git a/rtl/top/tinyriscv_soc_top.sv b/rtl/top/tinyriscv_soc_top.sv index 38adffd..07c7216 100644 --- a/rtl/top/tinyriscv_soc_top.sv +++ b/rtl/top/tinyriscv_soc_top.sv @@ -19,7 +19,8 @@ // tinyriscv soc顶层模块 module tinyriscv_soc_top #( - parameter bit TRACE_ENABLE = 1'b0 + parameter bit TRACE_ENABLE = 1'b0, + parameter int GPIO_NUM = 2 )( input wire clk_50m_i, // 时钟引脚 @@ -107,15 +108,19 @@ module tinyriscv_soc_top #( wire timer0_irq; wire uart0_irq; + wire gpio0_irq; + wire gpio1_irq; - wire[1:0] io_in; - wire[31:0] gpio_ctrl; - wire[31:0] gpio_data; + wire[GPIO_NUM-1:0] gpio_data_in; + wire[GPIO_NUM-1:0] gpio_oe; + wire[GPIO_NUM-1:0] gpio_data_out; always @ (*) begin irq_src = 32'h0; irq_src[0] = timer0_irq; irq_src[1] = uart0_irq; + irq_src[2] = gpio0_irq; + irq_src[3] = gpio1_irq; end `ifdef VERILATOR @@ -202,27 +207,32 @@ module tinyriscv_soc_top #( .data_o (slave_rdata[Timer0]) ); - // IO0 - assign gpio_pins[0] = (gpio_ctrl[1:0] == 2'b01)? gpio_data[0]: 1'bz; - assign io_in[0] = gpio_pins[0]; - // IO1 - assign gpio_pins[1] = (gpio_ctrl[3:2] == 2'b01)? gpio_data[1]: 1'bz; - assign io_in[1] = gpio_pins[1]; + for (genvar i = 0; i < GPIO_NUM; i = i + 1) begin : g_gpio_data + assign gpio_pins[i] = gpio_oe[i] ? gpio_data_out[i] : 1'bz; + assign gpio_data_in[i] = gpio_pins[i]; + end assign slave_addr_mask[Gpio] = `GPIO_ADDR_MASK; assign slave_addr_base[Gpio] = `GPIO_ADDR_BASE; // 4.GPIO模块 - gpio u_gpio( - .clk (clk), - .rst_n (ndmreset_n), - .addr_i (slave_addr[Gpio]), - .data_i (slave_wdata[Gpio]), - .sel_i (slave_be[Gpio]), - .we_i (slave_we[Gpio]), - .data_o (slave_rdata[Gpio]), - .io_pin_i(io_in), - .reg_ctrl(gpio_ctrl), - .reg_data(gpio_data) + gpio_top #( + .GPIO_NUM(GPIO_NUM) + ) u_gpio ( + .clk_i (clk), + .rst_ni (ndmreset_n), + .gpio_oe_o (gpio_oe), + .gpio_data_o (gpio_data_out), + .gpio_data_i (gpio_data_in), + .irq_gpio0_o (gpio0_irq), + .irq_gpio1_o (gpio1_irq), + .irq_gpio2_4_o (), + .irq_gpio5_7_o (), + .req_i (slave_req[Gpio]), + .we_i (slave_we[Gpio]), + .be_i (slave_be[Gpio]), + .addr_i (slave_addr[Gpio]), + .data_i (slave_wdata[Gpio]), + .data_o (slave_rdata[Gpio]) ); assign slave_addr_mask[Uart0] = `UART0_ADDR_MASK; diff --git a/rtl/utils/prim_filter.sv b/rtl/utils/prim_filter.sv new file mode 100644 index 0000000..59795cc --- /dev/null +++ b/rtl/utils/prim_filter.sv @@ -0,0 +1,51 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Primitive input filter, with enable. Configurable number of cycles. +// +// when in reset, stored vector is zero +// when enable is false, output is input +// when enable is true, output is stored value, +// new input must be opposite value from stored value for +// #Cycles before switching to new value. + +module prim_filter #(parameter int Cycles = 4) ( + input clk_i, + input rst_ni, + input enable_i, + input filter_i, + output filter_o +); + + logic [Cycles-1:0] stored_vector_q, stored_vector_d; + logic stored_value_q, update_stored_value; + logic unused_stored_vector_q_msb; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_value_q <= 1'b0; + end else if (update_stored_value) begin + stored_value_q <= filter_i; + end + end + + assign stored_vector_d = {stored_vector_q[Cycles-2:0],filter_i}; + assign unused_stored_vector_q_msb = stored_vector_q[Cycles-1]; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_vector_q <= {Cycles{1'b0}}; + end else begin + stored_vector_q <= stored_vector_d; + end + end + + assign update_stored_value = + (stored_vector_d == {Cycles{1'b0}}) | + (stored_vector_d == {Cycles{1'b1}}); + + assign filter_o = enable_i ? stored_value_q : filter_i; + +endmodule +