rtl:perips: add i2c master

Signed-off-by: liangkangnan <liangkangnan@163.com>
pull/4/head
liangkangnan 2021-08-19 09:43:12 +08:00
parent 6143d9ee6a
commit 2afcba47ea
9 changed files with 1243 additions and 3 deletions

View File

@ -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

View File

@ -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

86
rtl/perips/i2c/i2c.hjson Normal file
View File

@ -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)",
}
]
}
]
}

134
rtl/perips/i2c/i2c_core.sv Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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: <reg>_<field>_{wd|we|qs}
// or <reg>_{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

73
rtl/perips/i2c/i2c_top.sv Normal file
View File

@ -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

View File

@ -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;