From 2afcba47ea45d3c48eef9d508bf2e1dd3b3d3284 Mon Sep 17 00:00:00 2001 From: liangkangnan Date: Thu, 19 Aug 2021 09:43:12 +0800 Subject: [PATCH] rtl:perips: add i2c master Signed-off-by: liangkangnan --- rtl.flist | 5 + rtl/core/defines.sv | 3 + rtl/perips/i2c/i2c.hjson | 86 +++++++ rtl/perips/i2c/i2c_core.sv | 134 ++++++++++ rtl/perips/i2c/i2c_master.sv | 288 +++++++++++++++++++++ rtl/perips/i2c/i2c_reg_pkg.sv | 159 ++++++++++++ rtl/perips/i2c/i2c_reg_top.sv | 453 ++++++++++++++++++++++++++++++++++ rtl/perips/i2c/i2c_top.sv | 73 ++++++ rtl/top/tinyriscv_soc_top.sv | 45 +++- 9 files changed, 1243 insertions(+), 3 deletions(-) create mode 100644 rtl/perips/i2c/i2c.hjson create mode 100644 rtl/perips/i2c/i2c_core.sv create mode 100644 rtl/perips/i2c/i2c_master.sv create mode 100644 rtl/perips/i2c/i2c_reg_pkg.sv create mode 100644 rtl/perips/i2c/i2c_reg_top.sv create mode 100644 rtl/perips/i2c/i2c_top.sv diff --git a/rtl.flist b/rtl.flist index 900065b..0cfca9f 100644 --- a/rtl.flist +++ b/rtl.flist @@ -53,6 +53,11 @@ ../rtl/perips/rvic/rvic_reg_top.sv ../rtl/perips/rvic/rvic_core.sv ../rtl/perips/rvic/rvic_top.sv +../rtl/perips/i2c/i2c_reg_pkg.sv +../rtl/perips/i2c/i2c_reg_top.sv +../rtl/perips/i2c/i2c_core.sv +../rtl/perips/i2c/i2c_top.sv +../rtl/perips/i2c/i2c_master.sv ../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv diff --git a/rtl/core/defines.sv b/rtl/core/defines.sv index 65e35f2..2163721 100644 --- a/rtl/core/defines.sv +++ b/rtl/core/defines.sv @@ -40,6 +40,9 @@ // UART0 `define UART0_ADDR_MASK ~32'hffff `define UART0_ADDR_BASE 32'h50000000 +// I2C0 +`define I2C0_ADDR_MASK ~32'hffff +`define I2C0_ADDR_BASE 32'h60000000 // Interrupt controller `define RVIC_ADDR_MASK ~32'hffff `define RVIC_ADDR_BASE 32'hD0000000 diff --git a/rtl/perips/i2c/i2c.hjson b/rtl/perips/i2c/i2c.hjson new file mode 100644 index 0000000..85a04b3 --- /dev/null +++ b/rtl/perips/i2c/i2c.hjson @@ -0,0 +1,86 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ name: "i2c", + clocking: [{clock: "clk_i", reset: "rst_ni"}], + bus_interfaces: [ + { protocol: "tlul", direction: "device" } + ], + regwidth: "32", + registers: [ + { name: "CTRL", + desc: "I2C control register", + swaccess: "rw", + hwaccess: "hrw", + hwqe: "true", + fields: [ + { bits: "0", + name: "START", + desc: "I2C start", + } + { bits: "1", + name: "INT_EN", + desc: "I2C interrupt enable", + } + { bits: "2", + name: "INT_PENDING", + swaccess: "rw1c", + desc: "I2C interrupt pending", + } + { bits: "3", + name: "MODE", + desc: "I2C mode, 0: master, 1: slave", + } + { bits: "4", + name: "WRITE", + desc: "0: write, 1: read", + } + { bits: "5", + name: "ACK", + swaccess: "ro", + desc: "0: ack, 1: nack", + } + { bits: "6", + name: "ERROR", + swaccess: "ro", + desc: "0: no error, 1: error", + } + { bits: "31:16", + name: "CLK_DIV", + desc: "I2C clock divider count", + } + ] + } + { name: "MASTER_DATA", + desc: "I2C master transfer data register", + swaccess: "rw", + hwaccess: "hrw", + fields: [ + { bits: "7:0", + name: "ADDRESS", + desc: "I2C slave address", + } + { bits: "15:8", + name: "REGREG", + desc: "I2C write or read reg", + } + { bits: "23:16", + name: "DATA", + desc: "I2C write or read data", + } + ] + } + { name: "SLAVE_DATA", + desc: "I2C slave received data register", + swaccess: "ro", + hwaccess: "hrw", + hwext: "true", + hwre: "true", + fields: [ + { bits: "7:0", + desc: "I2C slave received data(fifo)", + } + ] + } + ] +} diff --git a/rtl/perips/i2c/i2c_core.sv b/rtl/perips/i2c/i2c_core.sv new file mode 100644 index 0000000..95ce126 --- /dev/null +++ b/rtl/perips/i2c/i2c_core.sv @@ -0,0 +1,134 @@ + /* + 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 i2c_core ( + input logic clk_i, + input logic rst_ni, + + output logic scl_o, + output logic scl_oe_o, + input logic scl_i, + output logic sda_o, + output logic sda_oe_o, + input logic sda_i, + + output logic irq_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 + ); + + import i2c_reg_pkg::*; + + i2c_reg_pkg::i2c_reg2hw_t reg2hw; + i2c_reg_pkg::i2c_hw2reg_t hw2reg; + + logic master_mode; + logic slave_mode; + logic op_write; + logic op_read; + logic start; + logic [15:0] clk_div; + logic int_enable; + logic [7:0] master_address; + logic [7:0] master_register; + logic [7:0] master_data; + logic master_ready, master_ready_q; + logic master_start; + logic master_error; + logic [7:0] master_read_data; + + assign master_mode = ~reg2hw.ctrl.mode.q; + assign slave_mode = reg2hw.ctrl.mode.q; + assign op_write = ~reg2hw.ctrl.write.q; + assign op_read = reg2hw.ctrl.write.q; + assign start = reg2hw.ctrl.start.q; + assign clk_div = reg2hw.ctrl.clk_div.q; + assign int_enable = reg2hw.ctrl.int_en.q; + + assign master_address = reg2hw.master_data.address.q; + assign master_register = reg2hw.master_data.regreg.q; + assign master_data = reg2hw.master_data.data.q; + + // 软件写1启动master传输 + assign master_start = reg2hw.ctrl.start.qe && reg2hw.ctrl.start.q && master_ready; + + // master传输完成后,硬件清start位 + assign hw2reg.ctrl.start.d = 1'b0; + // master传输完成上升沿脉冲 + assign hw2reg.ctrl.start.de = (~master_ready_q) && master_ready; + + // 传输完成产生中断pending + assign hw2reg.ctrl.int_pending.d = 1'b1; + assign hw2reg.ctrl.int_pending.de = int_enable && (~master_ready_q) && master_ready; + + // 传输完成并且是读操作,则更新master data + assign hw2reg.master_data.data.d = master_read_data; + assign hw2reg.master_data.data.de = op_read && (~master_ready_q) && master_ready; + + // 传输完成更新error + assign hw2reg.ctrl.error.d = master_error; + assign hw2reg.ctrl.error.de = (~master_ready_q) && master_ready; + + assign irq_o = reg2hw.ctrl.int_pending.q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + master_ready_q <= 1'b1; + end else begin + master_ready_q <= master_ready; + end + end + + i2c_master u_i2c_master ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .enable_i (master_mode), + .div_ratio_i (clk_div), + .read_i (op_read), + .slave_addr_i (master_address), + .slave_reg_i (master_register), + .slave_data_i (master_data), + .start_i (master_start), + .ready_o (master_ready), + .error_o (master_error), + .data_o (master_read_data), + .scl_i (scl_i), + .scl_o (scl_o), + .scl_oe_o (scl_oe_o), + .sda_i (sda_i), + .sda_o (sda_o), + .sda_oe_o (sda_oe_o) + ); + + i2c_reg_top u_i2c_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/i2c/i2c_master.sv b/rtl/perips/i2c/i2c_master.sv new file mode 100644 index 0000000..ed1eaa8 --- /dev/null +++ b/rtl/perips/i2c/i2c_master.sv @@ -0,0 +1,288 @@ + /* + 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 i2c_master ( + input logic clk_i, + input logic rst_ni, + + input logic enable_i, + input logic [15:0] div_ratio_i, + input logic read_i, + input logic [7:0] slave_addr_i, + input logic [7:0] slave_reg_i, + input logic [7:0] slave_data_i, + input logic start_i, + output logic ready_o, + output logic error_o, + output logic [7:0] data_o, + + input logic scl_i, + output logic scl_o, + output logic scl_oe_o, + input logic sda_i, + output logic sda_o, + output logic sda_oe_o + ); + + localparam S_IDLE = 6'b000001; + localparam S_START = 6'b000010; + localparam S_ADDR = 6'b000100; + localparam S_REG = 6'b001000; + localparam S_DATA = 6'b010000; + localparam S_STOP = 6'b100000; + + logic tick; + + logic error_d, error_q; + logic [7:0] data_d, data_q; + logic [4:0] edge_cnt_d, edge_cnt_q; + logic [7:0] shift_reg_d, shift_reg_q; + logic [5:0] state_d, state_q; + logic sda_d, sda_q; + logic scl_d, scl_q; + logic sda_oe_d, sda_oe_q; + logic scl_oe_d, scl_oe_q; + logic op_read_d, op_read_q; + + + always_comb begin + state_d = state_q; + shift_reg_d = shift_reg_q; + scl_d = scl_q; + sda_oe_d = sda_oe_q; + scl_oe_d = scl_oe_q; + sda_d = sda_q; + edge_cnt_d = edge_cnt_q; + data_d = data_q; + error_d = error_q; + op_read_d = op_read_q; + + if (!enable_i) begin + sda_d = 1'b0; + sda_oe_d = 1'b0; + scl_d = 1'b0; + scl_oe_d = 1'b0; + state_d = S_IDLE; + end else begin + case (state_q) + S_IDLE: begin + sda_d = 1'b1; + sda_oe_d = 1'b1; + scl_d = 1'b1; + scl_oe_d = 1'b1; + if (start_i) begin + state_d = S_START; + error_d = 1'b0; + data_d = '0; + op_read_d = 1'b0; + end + end + + S_START: begin + if (tick) begin + sda_d = 1'b0; + edge_cnt_d = '0; + state_d = S_ADDR; + shift_reg_d = {slave_addr_i[7:1], op_read_q}; + end + end + + S_ADDR: begin + if (tick) begin + scl_d = ~scl_q; + edge_cnt_d = edge_cnt_q + 1'b1; + // 下降沿释放SDA,准备接收ACK + if (edge_cnt_q == 5'd16) begin + sda_oe_d = 1'b0; + // 上升沿接收ACK + end else if (edge_cnt_q == 5'd17) begin + // NACK + if (sda_i) begin + error_d = 1'b1; + // ACK + end else begin + if (op_read_q) begin + state_d = S_DATA; + end else begin + state_d = S_REG; + shift_reg_d = slave_reg_i; + end + edge_cnt_d = '0; + end + // 最后一个下降沿 + end else if (edge_cnt_q == 5'd18) begin + sda_d = 1'b0; + sda_oe_d = 1'b1; + // 最后一个上升沿 + end else if (edge_cnt_q == 5'd19) begin + state_d = S_STOP; + end else begin + // 发数据 + if (scl_q) begin + // 左移一位(MSB first) + shift_reg_d = {shift_reg_q[6:0], 1'b1}; + sda_d = shift_reg_q[7]; + sda_oe_d = 1'b1; + end + end + end + end + + S_REG: begin + if (tick) begin + scl_d = ~scl_q; + edge_cnt_d = edge_cnt_q + 1'b1; + // 下降沿释放SDA,准备接收ACK + if (edge_cnt_q == 5'd16) begin + sda_oe_d = 1'b0; + // 上升沿接收ACK + end else if (edge_cnt_q == 5'd17) begin + // NACK + if (sda_i) begin + error_d = 1'b1; + // ACK + end else begin + // 写操作,转去S_DATA状态 + if (!read_i) begin + state_d = S_DATA; + shift_reg_d = slave_data_i; + edge_cnt_d = '0; + // 读操作,发送STOP信号 + end else begin + op_read_d = 1'b1; + end + end + // 最后一个下降沿 + end else if (edge_cnt_q == 5'd18) begin + sda_d = 1'b0; + sda_oe_d = 1'b1; + // 最后一个上升沿 + end else if (edge_cnt_q == 5'd19) begin + state_d = S_STOP; + end else begin + // 发数据 + if (scl_q) begin + // 左移一位(MSB first) + shift_reg_d = {shift_reg_q[6:0], 1'b1}; + sda_d = shift_reg_q[7]; + sda_oe_d = 1'b1; + end + end + end + end + + S_DATA: begin + if (tick) begin + scl_d = ~scl_q; + edge_cnt_d = edge_cnt_q + 1'b1; + // 下降沿释放SDA,准备接收ACK + if (edge_cnt_q == 5'd16) begin + sda_oe_d = 1'b0; + // 上升沿接收ACK + end else if (edge_cnt_q == 5'd17) begin + // NACK + if (sda_i ^ op_read_q) begin + error_d = 1'b1; + // ACK + end else begin + error_d = 1'b0; + end + op_read_d = 1'b0; + // 最后一个下降沿 + end else if (edge_cnt_q == 5'd18) begin + sda_d = 1'b0; + sda_oe_d = 1'b1; + // 最后一个上升沿 + end else if (edge_cnt_q == 5'd19) begin + state_d = S_STOP; + end else begin + // 读数据 + if (op_read_q && (~scl_q)) begin + data_d = {data_q[6:0], sda_i}; + // 发数据 + end else if ((~op_read_q) && scl_q) begin + // 左移一位(MSB first) + shift_reg_d = {shift_reg_q[6:0], 1'b1}; + sda_d = shift_reg_q[7]; + sda_oe_d = 1'b1; + end + end + end + end + + S_STOP: begin + if (tick) begin + sda_d = 1'b1; + if (op_read_q) begin + state_d = S_START; + end else begin + state_d = S_IDLE; + end + end + end + + default: ; + endcase + end + end + + assign scl_o = scl_q; + assign scl_oe_o = scl_oe_q; + assign sda_o = sda_q; + assign sda_oe_o = sda_oe_q; + assign data_o = data_q; + assign ready_o = (state_q == S_IDLE); + assign error_o = error_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + state_q <= S_IDLE; + shift_reg_q <= '0; + scl_q <= 1'b0; + sda_q <= 1'b0; + sda_oe_q <= 1'b0; + scl_oe_q <= 1'b0; + edge_cnt_q <= '0; + data_q <= '0; + error_q <= 1'b0; + op_read_q <= 1'b0; + end else begin + state_q <= state_d; + shift_reg_q <= shift_reg_d; + scl_q <= scl_d; + sda_q <= sda_d; + sda_oe_q <= sda_oe_d; + scl_oe_q <= scl_oe_d; + edge_cnt_q <= edge_cnt_d; + data_q <= data_d; + error_q <= error_d; + op_read_q <= op_read_d; + end + end + + logic [15:0] ratio = {1'b0, div_ratio_i[15:1]}; + + clk_div #( + .RATIO_WIDTH(16) + ) u_clk_div ( + .clk_i(clk_i), + .rst_ni(rst_ni || (~((state_q == S_IDLE) && start_i))), + .en_i(state_q != S_IDLE), + .ratio_i(ratio), + .clk_o(tick) + ); + +endmodule diff --git a/rtl/perips/i2c/i2c_reg_pkg.sv b/rtl/perips/i2c/i2c_reg_pkg.sv new file mode 100644 index 0000000..74e32f3 --- /dev/null +++ b/rtl/perips/i2c/i2c_reg_pkg.sv @@ -0,0 +1,159 @@ +// 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 i2c_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 4; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + struct packed { + logic q; + logic qe; + } start; + struct packed { + logic q; + logic qe; + } int_en; + struct packed { + logic q; + logic qe; + } int_pending; + struct packed { + logic q; + logic qe; + } mode; + struct packed { + logic q; + logic qe; + } write; + struct packed { + logic q; + logic qe; + } ack; + struct packed { + logic q; + logic qe; + } error; + struct packed { + logic [15:0] q; + logic qe; + } clk_div; + } i2c_reg2hw_ctrl_reg_t; + + typedef struct packed { + struct packed { + logic [7:0] q; + } address; + struct packed { + logic [7:0] q; + } regreg; + struct packed { + logic [7:0] q; + } data; + } i2c_reg2hw_master_data_reg_t; + + typedef struct packed { + logic [7:0] q; + logic re; + } i2c_reg2hw_slave_data_reg_t; + + typedef struct packed { + struct packed { + logic d; + logic de; + } start; + struct packed { + logic d; + logic de; + } int_en; + struct packed { + logic d; + logic de; + } int_pending; + struct packed { + logic d; + logic de; + } mode; + struct packed { + logic d; + logic de; + } write; + struct packed { + logic d; + logic de; + } ack; + struct packed { + logic d; + logic de; + } error; + struct packed { + logic [15:0] d; + logic de; + } clk_div; + } i2c_hw2reg_ctrl_reg_t; + + typedef struct packed { + struct packed { + logic [7:0] d; + logic de; + } address; + struct packed { + logic [7:0] d; + logic de; + } regreg; + struct packed { + logic [7:0] d; + logic de; + } data; + } i2c_hw2reg_master_data_reg_t; + + typedef struct packed { + logic [7:0] d; + } i2c_hw2reg_slave_data_reg_t; + + // Register -> HW type + typedef struct packed { + i2c_reg2hw_ctrl_reg_t ctrl; // [63:33] + i2c_reg2hw_master_data_reg_t master_data; // [32:9] + i2c_reg2hw_slave_data_reg_t slave_data; // [8:0] + } i2c_reg2hw_t; + + // HW -> register type + typedef struct packed { + i2c_hw2reg_ctrl_reg_t ctrl; // [65:35] + i2c_hw2reg_master_data_reg_t master_data; // [34:8] + i2c_hw2reg_slave_data_reg_t slave_data; // [7:0] + } i2c_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] I2C_CTRL_OFFSET = 4'h0; + parameter logic [BlockAw-1:0] I2C_MASTER_DATA_OFFSET = 4'h4; + parameter logic [BlockAw-1:0] I2C_SLAVE_DATA_OFFSET = 4'h8; + + // Reset values for hwext registers and their fields + parameter logic [7:0] I2C_SLAVE_DATA_RESVAL = 8'h0; + + // Register index + typedef enum int { + I2C_CTRL, + I2C_MASTER_DATA, + I2C_SLAVE_DATA + } i2c_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] I2C_PERMIT [3] = '{ + 4'b1111, // index[0] I2C_CTRL + 4'b0111, // index[1] I2C_MASTER_DATA + 4'b0001 // index[2] I2C_SLAVE_DATA + }; + +endpackage + diff --git a/rtl/perips/i2c/i2c_reg_top.sv b/rtl/perips/i2c/i2c_reg_top.sv new file mode 100644 index 0000000..7a57cbe --- /dev/null +++ b/rtl/perips/i2c/i2c_reg_top.sv @@ -0,0 +1,453 @@ +// 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 i2c_reg_top ( + input logic clk_i, + input logic rst_ni, + + // To HW + output i2c_reg_pkg::i2c_reg2hw_t reg2hw, // Write + input i2c_reg_pkg::i2c_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 i2c_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 ctrl_we; + logic ctrl_start_qs; + logic ctrl_start_wd; + logic ctrl_int_en_qs; + logic ctrl_int_en_wd; + logic ctrl_int_pending_qs; + logic ctrl_int_pending_wd; + logic ctrl_mode_qs; + logic ctrl_mode_wd; + logic ctrl_write_qs; + logic ctrl_write_wd; + logic ctrl_ack_qs; + logic ctrl_error_qs; + logic [15:0] ctrl_clk_div_qs; + logic [15:0] ctrl_clk_div_wd; + logic master_data_we; + logic [7:0] master_data_address_qs; + logic [7:0] master_data_address_wd; + logic [7:0] master_data_regreg_qs; + logic [7:0] master_data_regreg_wd; + logic [7:0] master_data_data_qs; + logic [7:0] master_data_data_wd; + logic slave_data_re; + logic [7:0] slave_data_qs; + + // Register instances + // R[ctrl]: V(False) + + // F[start]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_start ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_start_wd), + + // from internal hardware + .de (hw2reg.ctrl.start.de), + .d (hw2reg.ctrl.start.d), + + // to internal hardware + .qe (reg2hw.ctrl.start.qe), + .q (reg2hw.ctrl.start.q), + + // to register interface (read) + .qs (ctrl_start_qs) + ); + + + // F[int_en]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_int_en ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_int_en_wd), + + // from internal hardware + .de (hw2reg.ctrl.int_en.de), + .d (hw2reg.ctrl.int_en.d), + + // to internal hardware + .qe (reg2hw.ctrl.int_en.qe), + .q (reg2hw.ctrl.int_en.q), + + // to register interface (read) + .qs (ctrl_int_en_qs) + ); + + + // F[int_pending]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_ctrl_int_pending ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_int_pending_wd), + + // from internal hardware + .de (hw2reg.ctrl.int_pending.de), + .d (hw2reg.ctrl.int_pending.d), + + // to internal hardware + .qe (reg2hw.ctrl.int_pending.qe), + .q (reg2hw.ctrl.int_pending.q), + + // to register interface (read) + .qs (ctrl_int_pending_qs) + ); + + + // F[mode]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_mode ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_mode_wd), + + // from internal hardware + .de (hw2reg.ctrl.mode.de), + .d (hw2reg.ctrl.mode.d), + + // to internal hardware + .qe (reg2hw.ctrl.mode.qe), + .q (reg2hw.ctrl.mode.q), + + // to register interface (read) + .qs (ctrl_mode_qs) + ); + + + // F[write]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_write ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_write_wd), + + // from internal hardware + .de (hw2reg.ctrl.write.de), + .d (hw2reg.ctrl.write.d), + + // to internal hardware + .qe (reg2hw.ctrl.write.qe), + .q (reg2hw.ctrl.write.q), + + // to register interface (read) + .qs (ctrl_write_qs) + ); + + + // F[ack]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ctrl_ack ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.ctrl.ack.de), + .d (hw2reg.ctrl.ack.d), + + // to internal hardware + .qe (reg2hw.ctrl.ack.qe), + .q (reg2hw.ctrl.ack.q), + + // to register interface (read) + .qs (ctrl_ack_qs) + ); + + + // F[error]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ctrl_error ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (1'b0), + .wd ('0), + + // from internal hardware + .de (hw2reg.ctrl.error.de), + .d (hw2reg.ctrl.error.d), + + // to internal hardware + .qe (reg2hw.ctrl.error.qe), + .q (reg2hw.ctrl.error.q), + + // to register interface (read) + .qs (ctrl_error_qs) + ); + + + // F[clk_div]: 31:16 + prim_subreg #( + .DW (16), + .SWACCESS("RW"), + .RESVAL (16'h0) + ) u_ctrl_clk_div ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_clk_div_wd), + + // from internal hardware + .de (hw2reg.ctrl.clk_div.de), + .d (hw2reg.ctrl.clk_div.d), + + // to internal hardware + .qe (reg2hw.ctrl.clk_div.qe), + .q (reg2hw.ctrl.clk_div.q), + + // to register interface (read) + .qs (ctrl_clk_div_qs) + ); + + + // R[master_data]: V(False) + + // F[address]: 7:0 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'h0) + ) u_master_data_address ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (master_data_we), + .wd (master_data_address_wd), + + // from internal hardware + .de (hw2reg.master_data.address.de), + .d (hw2reg.master_data.address.d), + + // to internal hardware + .qe (), + .q (reg2hw.master_data.address.q), + + // to register interface (read) + .qs (master_data_address_qs) + ); + + + // F[regreg]: 15:8 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'h0) + ) u_master_data_regreg ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (master_data_we), + .wd (master_data_regreg_wd), + + // from internal hardware + .de (hw2reg.master_data.regreg.de), + .d (hw2reg.master_data.regreg.d), + + // to internal hardware + .qe (), + .q (reg2hw.master_data.regreg.q), + + // to register interface (read) + .qs (master_data_regreg_qs) + ); + + + // F[data]: 23:16 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'h0) + ) u_master_data_data ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (master_data_we), + .wd (master_data_data_wd), + + // from internal hardware + .de (hw2reg.master_data.data.de), + .d (hw2reg.master_data.data.d), + + // to internal hardware + .qe (), + .q (reg2hw.master_data.data.q), + + // to register interface (read) + .qs (master_data_data_qs) + ); + + + // R[slave_data]: V(True) + + prim_subreg_ext #( + .DW (8) + ) u_slave_data ( + .re (slave_data_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.slave_data.d), + .qre (reg2hw.slave_data.re), + .qe (), + .q (reg2hw.slave_data.q), + .qs (slave_data_qs) + ); + + + logic [2:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == I2C_CTRL_OFFSET); + addr_hit[1] = (reg_addr == I2C_MASTER_DATA_OFFSET); + addr_hit[2] = (reg_addr == I2C_SLAVE_DATA_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] & (|(I2C_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(I2C_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(I2C_PERMIT[2] & ~reg_be))))); + end + + assign ctrl_we = addr_hit[0] & reg_we & !reg_error; + + assign ctrl_start_wd = reg_wdata[0]; + + assign ctrl_int_en_wd = reg_wdata[1]; + + assign ctrl_int_pending_wd = reg_wdata[2]; + + assign ctrl_mode_wd = reg_wdata[3]; + + assign ctrl_write_wd = reg_wdata[4]; + + assign ctrl_clk_div_wd = reg_wdata[31:16]; + assign master_data_we = addr_hit[1] & reg_we & !reg_error; + + assign master_data_address_wd = reg_wdata[7:0]; + + assign master_data_regreg_wd = reg_wdata[15:8]; + + assign master_data_data_wd = reg_wdata[23:16]; + assign slave_data_re = addr_hit[2] & reg_re & !reg_error; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = ctrl_start_qs; + reg_rdata_next[1] = ctrl_int_en_qs; + reg_rdata_next[2] = ctrl_int_pending_qs; + reg_rdata_next[3] = ctrl_mode_qs; + reg_rdata_next[4] = ctrl_write_qs; + reg_rdata_next[5] = ctrl_ack_qs; + reg_rdata_next[6] = ctrl_error_qs; + reg_rdata_next[31:16] = ctrl_clk_div_qs; + end + + addr_hit[1]: begin + reg_rdata_next[7:0] = master_data_address_qs; + reg_rdata_next[15:8] = master_data_regreg_qs; + reg_rdata_next[23:16] = master_data_data_qs; + end + + addr_hit[2]: begin + reg_rdata_next[7:0] = slave_data_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/i2c/i2c_top.sv b/rtl/perips/i2c/i2c_top.sv new file mode 100644 index 0000000..652ddd5 --- /dev/null +++ b/rtl/perips/i2c/i2c_top.sv @@ -0,0 +1,73 @@ + /* + 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 i2c_top ( + input logic clk_i, + input logic rst_ni, + + output logic scl_o, + output logic scl_oe_o, + input logic scl_i, + output logic sda_o, + output logic sda_oe_o, + input logic sda_i, + + output logic irq_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]}; + + i2c_core u_i2c_core ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .scl_o (scl_o), + .scl_oe_o (scl_oe_o), + .scl_i (scl_i), + .sda_o (sda_o), + .sda_oe_o (sda_oe_o), + .sda_i (sda_i), + .irq_o (irq_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 c6c0e96..2d5f986 100644 --- a/rtl/top/tinyriscv_soc_top.sv +++ b/rtl/top/tinyriscv_soc_top.sv @@ -31,6 +31,9 @@ module tinyriscv_soc_top #( output wire uart_tx_pin, // UART发送引脚 input wire uart_rx_pin, // UART接收引脚 + inout wire i2c_scl_pin, // I2C SCL引脚 + inout wire i2c_sda_pin, // I2C SDA引脚 + inout wire[1:0] gpio_pins, // GPIO引脚,1bit代表一个GPIO input wire jtag_TCK_pin, // JTAG TCK引脚 @@ -42,9 +45,9 @@ module tinyriscv_soc_top #( localparam int MASTERS = 3; // Number of master ports `ifdef VERILATOR - localparam int SLAVES = 8; // Number of slave ports + localparam int SLAVES = 9; // Number of slave ports `else - localparam int SLAVES = 7; // Number of slave ports + localparam int SLAVES = 8; // Number of slave ports `endif // masters @@ -60,8 +63,9 @@ module tinyriscv_soc_top #( localparam int Gpio = 4; localparam int Uart0 = 5; localparam int Rvic = 6; + localparam int I2c = 7; `ifdef VERILATOR - localparam int SimCtrl = 7; + localparam int SimCtrl = 8; `endif @@ -110,17 +114,26 @@ module tinyriscv_soc_top #( wire uart0_irq; wire gpio0_irq; wire gpio1_irq; + wire i2c0_irq; wire[GPIO_NUM-1:0] gpio_data_in; wire[GPIO_NUM-1:0] gpio_oe; wire[GPIO_NUM-1:0] gpio_data_out; + wire i2c_scl_in; + wire i2c_scl_oe; + wire i2c_scl_out; + wire i2c_sda_in; + wire i2c_sda_oe; + wire i2c_sda_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; + irq_src[4] = i2c0_irq; end `ifdef VERILATOR @@ -269,6 +282,32 @@ module tinyriscv_soc_top #( .data_o (slave_rdata[Rvic]) ); + assign i2c_scl_pin = i2c_scl_oe ? i2c_scl_out : 1'bz; + assign i2c_scl_in = i2c_scl_pin; + assign i2c_sda_pin = i2c_sda_oe ? i2c_sda_out : 1'bz; + assign i2c_sda_in = i2c_sda_pin; + + assign slave_addr_mask[I2c] = `I2C0_ADDR_MASK; + assign slave_addr_base[I2c] = `I2C0_ADDR_BASE; + // 7.I2C0模块 + i2c_top i2c0( + .clk_i (clk), + .rst_ni (ndmreset_n), + .scl_o (i2c_scl_out), + .scl_oe_o (i2c_scl_oe), + .scl_i (i2c_scl_in), + .sda_o (i2c_sda_out), + .sda_oe_o (i2c_sda_oe), + .sda_i (i2c_sda_in), + .irq_o (i2c0_irq), + .req_i (slave_req[I2c]), + .we_i (slave_we[I2c]), + .be_i (slave_be[I2c]), + .addr_i (slave_addr[I2c]), + .data_i (slave_wdata[I2c]), + .data_o (slave_rdata[I2c]) + ); + `ifdef VERILATOR assign slave_addr_mask[SimCtrl] = `SIM_CTRL_ADDR_MASK; assign slave_addr_base[SimCtrl] = `SIM_CTRL_ADDR_BASE;