rtl:perips:i2c: add i2c slave

Signed-off-by: liangkangnan <liangkangnan@163.com>
pull/4/head
liangkangnan 2021-08-25 17:51:35 +08:00
parent 7e57d8db17
commit e708eb6d4d
6 changed files with 980 additions and 101 deletions

View File

@ -58,6 +58,7 @@
../rtl/perips/i2c/i2c_core.sv ../rtl/perips/i2c/i2c_core.sv
../rtl/perips/i2c/i2c_top.sv ../rtl/perips/i2c/i2c_top.sv
../rtl/perips/i2c/i2c_master.sv ../rtl/perips/i2c/i2c_master.sv
../rtl/perips/i2c/i2c_slave.sv
../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect.sv
../rtl/sys_bus/obi_interconnect_master_sel.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv

View File

@ -36,15 +36,23 @@
desc: "0: write, 1: read", desc: "0: write, 1: read",
} }
{ bits: "5", { bits: "5",
name: "ACK",
swaccess: "ro",
desc: "0: ack, 1: nack",
}
{ bits: "6",
name: "ERROR", name: "ERROR",
swaccess: "ro", swaccess: "ro",
desc: "0: no error, 1: error", desc: "0: no error, 1: error",
} }
{ bits: "6",
name: "SLAVE_WR",
swaccess: "ro",
desc: "0: write, 1: read",
}
{ bits: "7",
name: "SLAVE_RDY",
desc: "0: not ready, 1: ready",
}
{ bits: "15:8",
name: "SLAVE_ADDR",
desc: "I2C slave address",
}
{ bits: "31:16", { bits: "31:16",
name: "CLK_DIV", name: "CLK_DIV",
desc: "I2C clock divider count", desc: "I2C clock divider count",
@ -70,15 +78,59 @@
} }
] ]
} }
{ name: "SLAVE_DATA", { name: "SLAVE_ADDR",
desc: "I2C slave received data register", desc: "I2C slave read or write address register",
swaccess: "ro", swaccess: "ro",
hwaccess: "hrw", hwaccess: "hrw",
hwext: "true",
hwre: "true",
fields: [ fields: [
{ bits: "7:0", { bits: "7:0",
desc: "I2C slave received data(fifo)", name: "ADDR0",
desc: "I2C slave read or write address[7:0]",
}
{ bits: "15:8",
name: "ADDR1",
desc: "I2C slave read or write address[15:8]",
}
{ bits: "23:16",
name: "ADDR2",
desc: "I2C slave read or write address[23:16]",
}
{ bits: "31:24",
name: "ADDR3",
desc: "I2C slave read or write address[31:24]",
}
]
}
{ name: "SLAVE_WDATA",
desc: "I2C slave write data register",
swaccess: "ro",
hwaccess: "hrw",
fields: [
{ bits: "7:0",
name: "WDATA0",
desc: "I2C slave write data[7:0]",
}
{ bits: "15:8",
name: "WDATA1",
desc: "I2C slave write data[15:8]",
}
{ bits: "23:16",
name: "WDATA2",
desc: "I2C slave write data[23:16]",
}
{ bits: "31:24",
name: "WDATA3",
desc: "I2C slave write data[31:24]",
}
]
}
{ name: "SLAVE_RDATA",
desc: "I2C slave read data register",
swaccess: "rw",
hwaccess: "hro",
fields: [
{ bits: "31:0",
desc: "I2C slave read data",
} }
] ]
} }

View File

@ -41,7 +41,6 @@ module i2c_core (
i2c_reg_pkg::i2c_hw2reg_t hw2reg; i2c_reg_pkg::i2c_hw2reg_t hw2reg;
logic master_mode; logic master_mode;
logic slave_mode;
logic op_write; logic op_write;
logic op_read; logic op_read;
logic start; logic start;
@ -50,13 +49,36 @@ module i2c_core (
logic [7:0] master_address; logic [7:0] master_address;
logic [7:0] master_register; logic [7:0] master_register;
logic [7:0] master_data; logic [7:0] master_data;
logic master_ready, master_ready_q; logic master_ready, master_ready_re;
logic master_start; logic master_start;
logic master_error; logic master_error;
logic [7:0] master_read_data; logic [7:0] master_read_data;
logic master_scl;
logic master_scl_oe;
logic master_sda;
logic master_sda_oe;
logic slave_mode;
logic slave_start;
logic [7:0] slave_address;
logic [7:0] slave_recv_address;
logic [7:0] slave_send_data;
logic [7:0] slave_recv_data;
logic slave_recv_read;
logic slave_recv_valid, slave_recv_valid_re;
logic slave_op_req, slave_op_req_re;
logic slave_scl;
logic slave_scl_oe;
logic slave_sda;
logic slave_sda_oe;
assign scl_o = master_scl | slave_scl;
assign scl_oe_o = master_scl_oe | slave_scl_oe;
assign sda_o = master_sda | slave_sda;
assign sda_oe_o = master_sda_oe | slave_sda_oe;
//////////////////////////////////////////////////////// master //////////////////////////////////////////////////////////
assign master_mode = ~reg2hw.ctrl.mode.q; assign master_mode = ~reg2hw.ctrl.mode.q;
assign slave_mode = reg2hw.ctrl.mode.q;
assign op_write = ~reg2hw.ctrl.write.q; assign op_write = ~reg2hw.ctrl.write.q;
assign op_read = reg2hw.ctrl.write.q; assign op_read = reg2hw.ctrl.write.q;
assign start = reg2hw.ctrl.start.q; assign start = reg2hw.ctrl.start.q;
@ -68,35 +90,152 @@ module i2c_core (
assign master_data = reg2hw.master_data.data.q; assign master_data = reg2hw.master_data.data.q;
// 软件写1启动master传输 // 软件写1启动master传输
assign master_start = reg2hw.ctrl.start.qe && reg2hw.ctrl.start.q && master_ready; assign master_start = reg2hw.ctrl.start.qe && reg2hw.ctrl.start.q && master_ready && master_mode;
// master传输完成硬件清start位 // master传输完成硬件清start位
assign hw2reg.ctrl.start.d = 1'b0; assign hw2reg.ctrl.start.d = 1'b0;
// master传输完成上升沿脉冲 assign hw2reg.ctrl.start.de = master_ready_re;
assign hw2reg.ctrl.start.de = (~master_ready_q) && master_ready;
// 传输完成产生中断pending // 传输完成产生中断pending
assign hw2reg.ctrl.int_pending.d = 1'b1; assign hw2reg.ctrl.int_pending.d = 1'b1;
assign hw2reg.ctrl.int_pending.de = int_enable && (~master_ready_q) && master_ready; assign hw2reg.ctrl.int_pending.de = int_enable && (master_ready_re || slave_op_req_re);
// 传输完成并且是读操作则更新master data // 传输完成并且是读操作则更新master data
assign hw2reg.master_data.data.d = master_read_data; assign hw2reg.master_data.data.d = master_read_data;
assign hw2reg.master_data.data.de = op_read && (~master_ready_q) && master_ready; assign hw2reg.master_data.data.de = op_read && master_ready_re;
// 传输完成更新error // 传输完成更新error
assign hw2reg.ctrl.error.d = master_error; assign hw2reg.ctrl.error.d = master_error;
assign hw2reg.ctrl.error.de = (~master_ready_q) && master_ready; assign hw2reg.ctrl.error.de = master_ready_re;
assign irq_o = reg2hw.ctrl.int_pending.q; assign irq_o = reg2hw.ctrl.int_pending.q;
always_ff @(posedge clk_i or negedge rst_ni) begin edge_detect master_ready_edge_detect (
if (~rst_ni) begin .clk_i (clk_i),
master_ready_q <= 1'b1; .rst_ni (rst_ni),
end else begin .sig_i (master_ready),
master_ready_q <= master_ready; .sig_o (),
.re_o (master_ready_re),
.fe_o ()
);
//////////////////////////////////////////////////////// slave //////////////////////////////////////////////////////////
assign slave_start = reg2hw.ctrl.start.q;
assign slave_mode = reg2hw.ctrl.mode.q;
assign slave_address = reg2hw.ctrl.slave_addr.q;
// 收到请求后清ready状态
assign hw2reg.ctrl.slave_rdy.d = 1'b0;
assign hw2reg.ctrl.slave_rdy.de = slave_op_req_re;
always_comb begin
slave_send_data = '0;
hw2reg.slave_addr.addr0.de = 1'b0;
hw2reg.slave_addr.addr0.d = '0;
hw2reg.slave_addr.addr1.de = 1'b0;
hw2reg.slave_addr.addr1.d = '0;
hw2reg.slave_addr.addr2.de = 1'b0;
hw2reg.slave_addr.addr2.d = '0;
hw2reg.slave_addr.addr3.de = 1'b0;
hw2reg.slave_addr.addr3.d = '0;
hw2reg.slave_wdata.wdata0.de = 1'b0;
hw2reg.slave_wdata.wdata0.d = '0;
hw2reg.slave_wdata.wdata1.de = 1'b0;
hw2reg.slave_wdata.wdata1.d = '0;
hw2reg.slave_wdata.wdata2.de = 1'b0;
hw2reg.slave_wdata.wdata2.d = '0;
hw2reg.slave_wdata.wdata3.de = 1'b0;
hw2reg.slave_wdata.wdata3.d = '0;
hw2reg.ctrl.slave_wr.de = 1'b0;
hw2reg.ctrl.slave_wr.d = '0;
case (slave_recv_address)
8'h0: begin
slave_send_data = {6'h0, reg2hw.ctrl.slave_rdy.q, 1'b0};
end
8'hc: begin
slave_send_data = reg2hw.slave_rdata.q[7:0];
end
8'hd: begin
slave_send_data = reg2hw.slave_rdata.q[15:8];
end
8'he: begin
slave_send_data = reg2hw.slave_rdata.q[23:16];
end
8'hf: begin
slave_send_data = reg2hw.slave_rdata.q[31:24];
end
default: ;
endcase
// 收到写请求
if (slave_recv_valid_re && (!slave_recv_read)) begin
case (slave_recv_address)
8'h0: begin
hw2reg.ctrl.slave_wr.de = 1'b1;
hw2reg.ctrl.slave_wr.d = slave_recv_data[0];
end
8'h4: begin
hw2reg.slave_addr.addr0.de = 1'b1;
hw2reg.slave_addr.addr0.d = slave_recv_data;
end
8'h5: begin
hw2reg.slave_addr.addr1.de = 1'b1;
hw2reg.slave_addr.addr1.d = slave_recv_data;
end
8'h6: begin
hw2reg.slave_addr.addr2.de = 1'b1;
hw2reg.slave_addr.addr2.d = slave_recv_data;
end
8'h7: begin
hw2reg.slave_addr.addr3.de = 1'b1;
hw2reg.slave_addr.addr3.d = slave_recv_data;
end
8'h8: begin
hw2reg.slave_wdata.wdata0.de = 1'b1;
hw2reg.slave_wdata.wdata0.d = slave_recv_data;
end
8'h9: begin
hw2reg.slave_wdata.wdata1.de = 1'b1;
hw2reg.slave_wdata.wdata1.d = slave_recv_data;
end
8'ha: begin
hw2reg.slave_wdata.wdata2.de = 1'b1;
hw2reg.slave_wdata.wdata2.d = slave_recv_data;
end
8'hb: begin
hw2reg.slave_wdata.wdata3.de = 1'b1;
hw2reg.slave_wdata.wdata3.d = slave_recv_data;
end
default: ;
endcase
end end
end end
// master写0x00地址发出中断(通知软件)
assign slave_op_req = slave_recv_valid_re && (!slave_recv_read) && (slave_recv_address == 8'h0);
// 软件收到请求上升沿检测
edge_detect slave_op_req_edge_detect (
.clk_i (clk_i),
.rst_ni (rst_ni),
.sig_i (slave_op_req),
.sig_o (),
.re_o (slave_op_req_re),
.fe_o ()
);
// slave收到请求上升沿检测
edge_detect slave_recv_valid_edge_detect (
.clk_i (clk_i),
.rst_ni (rst_ni),
.sig_i (slave_recv_valid),
.sig_o (),
.re_o (slave_recv_valid_re),
.fe_o ()
);
i2c_master u_i2c_master ( i2c_master u_i2c_master (
.clk_i (clk_i), .clk_i (clk_i),
.rst_ni (rst_ni), .rst_ni (rst_ni),
@ -111,11 +250,29 @@ module i2c_core (
.error_o (master_error), .error_o (master_error),
.data_o (master_read_data), .data_o (master_read_data),
.scl_i (scl_i), .scl_i (scl_i),
.scl_o (scl_o), .scl_o (master_scl),
.scl_oe_o (scl_oe_o), .scl_oe_o (master_scl_oe),
.sda_i (sda_i), .sda_i (sda_i),
.sda_o (sda_o), .sda_o (master_sda),
.sda_oe_o (sda_oe_o) .sda_oe_o (master_sda_oe)
);
i2c_slave u_i2c_slave (
.clk_i (clk_i),
.rst_ni (rst_ni),
.enable_i (slave_mode & slave_start),
.slave_addr_i (slave_address),
.data_i (slave_send_data),
.addr_o (slave_recv_address),
.read_o (slave_recv_read),
.valid_o (slave_recv_valid),
.data_o (slave_recv_data),
.scl_i (scl_i),
.scl_o (slave_scl),
.scl_oe_o (slave_scl_oe),
.sda_i (sda_i),
.sda_o (slave_sda),
.sda_oe_o (slave_sda_oe)
); );
i2c_reg_top u_i2c_reg_top ( i2c_reg_top u_i2c_reg_top (

View File

@ -7,7 +7,7 @@
package i2c_reg_pkg; package i2c_reg_pkg;
// Address widths within the block // Address widths within the block
parameter int BlockAw = 4; parameter int BlockAw = 5;
//////////////////////////// ////////////////////////////
// Typedefs for registers // // Typedefs for registers //
@ -37,11 +37,19 @@ package i2c_reg_pkg;
struct packed { struct packed {
logic q; logic q;
logic qe; logic qe;
} ack; } error;
struct packed { struct packed {
logic q; logic q;
logic qe; logic qe;
} error; } slave_wr;
struct packed {
logic q;
logic qe;
} slave_rdy;
struct packed {
logic [7:0] q;
logic qe;
} slave_addr;
struct packed { struct packed {
logic [15:0] q; logic [15:0] q;
logic qe; logic qe;
@ -61,9 +69,38 @@ package i2c_reg_pkg;
} i2c_reg2hw_master_data_reg_t; } i2c_reg2hw_master_data_reg_t;
typedef struct packed { typedef struct packed {
struct packed {
logic [7:0] q; logic [7:0] q;
logic re; } addr0;
} i2c_reg2hw_slave_data_reg_t; struct packed {
logic [7:0] q;
} addr1;
struct packed {
logic [7:0] q;
} addr2;
struct packed {
logic [7:0] q;
} addr3;
} i2c_reg2hw_slave_addr_reg_t;
typedef struct packed {
struct packed {
logic [7:0] q;
} wdata0;
struct packed {
logic [7:0] q;
} wdata1;
struct packed {
logic [7:0] q;
} wdata2;
struct packed {
logic [7:0] q;
} wdata3;
} i2c_reg2hw_slave_wdata_reg_t;
typedef struct packed {
logic [31:0] q;
} i2c_reg2hw_slave_rdata_reg_t;
typedef struct packed { typedef struct packed {
struct packed { struct packed {
@ -89,11 +126,19 @@ package i2c_reg_pkg;
struct packed { struct packed {
logic d; logic d;
logic de; logic de;
} ack; } error;
struct packed { struct packed {
logic d; logic d;
logic de; logic de;
} error; } slave_wr;
struct packed {
logic d;
logic de;
} slave_rdy;
struct packed {
logic [7:0] d;
logic de;
} slave_addr;
struct packed { struct packed {
logic [15:0] d; logic [15:0] d;
logic de; logic de;
@ -116,43 +161,83 @@ package i2c_reg_pkg;
} i2c_hw2reg_master_data_reg_t; } i2c_hw2reg_master_data_reg_t;
typedef struct packed { typedef struct packed {
struct packed {
logic [7:0] d; logic [7:0] d;
} i2c_hw2reg_slave_data_reg_t; logic de;
} addr0;
struct packed {
logic [7:0] d;
logic de;
} addr1;
struct packed {
logic [7:0] d;
logic de;
} addr2;
struct packed {
logic [7:0] d;
logic de;
} addr3;
} i2c_hw2reg_slave_addr_reg_t;
typedef struct packed {
struct packed {
logic [7:0] d;
logic de;
} wdata0;
struct packed {
logic [7:0] d;
logic de;
} wdata1;
struct packed {
logic [7:0] d;
logic de;
} wdata2;
struct packed {
logic [7:0] d;
logic de;
} wdata3;
} i2c_hw2reg_slave_wdata_reg_t;
// Register -> HW type // Register -> HW type
typedef struct packed { typedef struct packed {
i2c_reg2hw_ctrl_reg_t ctrl; // [63:33] i2c_reg2hw_ctrl_reg_t ctrl; // [161:120]
i2c_reg2hw_master_data_reg_t master_data; // [32:9] i2c_reg2hw_master_data_reg_t master_data; // [119:96]
i2c_reg2hw_slave_data_reg_t slave_data; // [8:0] i2c_reg2hw_slave_addr_reg_t slave_addr; // [95:64]
i2c_reg2hw_slave_wdata_reg_t slave_wdata; // [63:32]
i2c_reg2hw_slave_rdata_reg_t slave_rdata; // [31:0]
} i2c_reg2hw_t; } i2c_reg2hw_t;
// HW -> register type // HW -> register type
typedef struct packed { typedef struct packed {
i2c_hw2reg_ctrl_reg_t ctrl; // [65:35] i2c_hw2reg_ctrl_reg_t ctrl; // [140:99]
i2c_hw2reg_master_data_reg_t master_data; // [34:8] i2c_hw2reg_master_data_reg_t master_data; // [98:72]
i2c_hw2reg_slave_data_reg_t slave_data; // [7:0] i2c_hw2reg_slave_addr_reg_t slave_addr; // [71:36]
i2c_hw2reg_slave_wdata_reg_t slave_wdata; // [35:0]
} i2c_hw2reg_t; } i2c_hw2reg_t;
// Register offsets // Register offsets
parameter logic [BlockAw-1:0] I2C_CTRL_OFFSET = 4'h0; parameter logic [BlockAw-1:0] I2C_CTRL_OFFSET = 5'h0;
parameter logic [BlockAw-1:0] I2C_MASTER_DATA_OFFSET = 4'h4; parameter logic [BlockAw-1:0] I2C_MASTER_DATA_OFFSET = 5'h4;
parameter logic [BlockAw-1:0] I2C_SLAVE_DATA_OFFSET = 4'h8; parameter logic [BlockAw-1:0] I2C_SLAVE_ADDR_OFFSET = 5'h8;
parameter logic [BlockAw-1:0] I2C_SLAVE_WDATA_OFFSET = 5'hc;
// Reset values for hwext registers and their fields parameter logic [BlockAw-1:0] I2C_SLAVE_RDATA_OFFSET = 5'h10;
parameter logic [7:0] I2C_SLAVE_DATA_RESVAL = 8'h0;
// Register index // Register index
typedef enum int { typedef enum int {
I2C_CTRL, I2C_CTRL,
I2C_MASTER_DATA, I2C_MASTER_DATA,
I2C_SLAVE_DATA I2C_SLAVE_ADDR,
I2C_SLAVE_WDATA,
I2C_SLAVE_RDATA
} i2c_id_e; } i2c_id_e;
// Register width information to check illegal writes // Register width information to check illegal writes
parameter logic [3:0] I2C_PERMIT [3] = '{ parameter logic [3:0] I2C_PERMIT [5] = '{
4'b1111, // index[0] I2C_CTRL 4'b1111, // index[0] I2C_CTRL
4'b0111, // index[1] I2C_MASTER_DATA 4'b0111, // index[1] I2C_MASTER_DATA
4'b0001 // index[2] I2C_SLAVE_DATA 4'b1111, // index[2] I2C_SLAVE_ADDR
4'b1111, // index[3] I2C_SLAVE_WDATA
4'b1111 // index[4] I2C_SLAVE_RDATA
}; };
endpackage endpackage

View File

@ -23,7 +23,7 @@ module i2c_reg_top (
import i2c_reg_pkg::* ; import i2c_reg_pkg::* ;
localparam int AW = 4; localparam int AW = 5;
localparam int DW = 32; localparam int DW = 32;
localparam int DBW = DW/8; // Byte Width localparam int DBW = DW/8; // Byte Width
@ -49,8 +49,12 @@ module i2c_reg_top (
logic ctrl_mode_wd; logic ctrl_mode_wd;
logic ctrl_write_qs; logic ctrl_write_qs;
logic ctrl_write_wd; logic ctrl_write_wd;
logic ctrl_ack_qs;
logic ctrl_error_qs; logic ctrl_error_qs;
logic ctrl_slave_wr_qs;
logic ctrl_slave_rdy_qs;
logic ctrl_slave_rdy_wd;
logic [7:0] ctrl_slave_addr_qs;
logic [7:0] ctrl_slave_addr_wd;
logic [15:0] ctrl_clk_div_qs; logic [15:0] ctrl_clk_div_qs;
logic [15:0] ctrl_clk_div_wd; logic [15:0] ctrl_clk_div_wd;
logic master_data_we; logic master_data_we;
@ -60,8 +64,17 @@ module i2c_reg_top (
logic [7:0] master_data_regreg_wd; logic [7:0] master_data_regreg_wd;
logic [7:0] master_data_data_qs; logic [7:0] master_data_data_qs;
logic [7:0] master_data_data_wd; logic [7:0] master_data_data_wd;
logic slave_data_re; logic [7:0] slave_addr_addr0_qs;
logic [7:0] slave_data_qs; logic [7:0] slave_addr_addr1_qs;
logic [7:0] slave_addr_addr2_qs;
logic [7:0] slave_addr_addr3_qs;
logic [7:0] slave_wdata_wdata0_qs;
logic [7:0] slave_wdata_wdata1_qs;
logic [7:0] slave_wdata_wdata2_qs;
logic [7:0] slave_wdata_wdata3_qs;
logic slave_rdata_we;
logic [31:0] slave_rdata_qs;
logic [31:0] slave_rdata_wd;
// Register instances // Register instances
// R[ctrl]: V(False) // R[ctrl]: V(False)
@ -196,33 +209,7 @@ module i2c_reg_top (
); );
// F[ack]: 5:5 // F[error]: 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 #( prim_subreg #(
.DW (1), .DW (1),
.SWACCESS("RO"), .SWACCESS("RO"),
@ -248,6 +235,84 @@ module i2c_reg_top (
); );
// F[slave_wr]: 6:6
prim_subreg #(
.DW (1),
.SWACCESS("RO"),
.RESVAL (1'h0)
) u_ctrl_slave_wr (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.ctrl.slave_wr.de),
.d (hw2reg.ctrl.slave_wr.d),
// to internal hardware
.qe (reg2hw.ctrl.slave_wr.qe),
.q (reg2hw.ctrl.slave_wr.q),
// to register interface (read)
.qs (ctrl_slave_wr_qs)
);
// F[slave_rdy]: 7:7
prim_subreg #(
.DW (1),
.SWACCESS("RW"),
.RESVAL (1'h0)
) u_ctrl_slave_rdy (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl_we),
.wd (ctrl_slave_rdy_wd),
// from internal hardware
.de (hw2reg.ctrl.slave_rdy.de),
.d (hw2reg.ctrl.slave_rdy.d),
// to internal hardware
.qe (reg2hw.ctrl.slave_rdy.qe),
.q (reg2hw.ctrl.slave_rdy.q),
// to register interface (read)
.qs (ctrl_slave_rdy_qs)
);
// F[slave_addr]: 15:8
prim_subreg #(
.DW (8),
.SWACCESS("RW"),
.RESVAL (8'h0)
) u_ctrl_slave_addr (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (ctrl_we),
.wd (ctrl_slave_addr_wd),
// from internal hardware
.de (hw2reg.ctrl.slave_addr.de),
.d (hw2reg.ctrl.slave_addr.d),
// to internal hardware
.qe (reg2hw.ctrl.slave_addr.qe),
.q (reg2hw.ctrl.slave_addr.q),
// to register interface (read)
.qs (ctrl_slave_addr_qs)
);
// F[clk_div]: 31:16 // F[clk_div]: 31:16
prim_subreg #( prim_subreg #(
.DW (16), .DW (16),
@ -354,28 +419,253 @@ module i2c_reg_top (
); );
// R[slave_data]: V(True) // R[slave_addr]: V(False)
prim_subreg_ext #( // F[addr0]: 7:0
.DW (8) prim_subreg #(
) u_slave_data ( .DW (8),
.re (slave_data_re), .SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_addr_addr0 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0), .we (1'b0),
.wd ('0), .wd ('0),
.d (hw2reg.slave_data.d),
.qre (reg2hw.slave_data.re), // from internal hardware
.de (hw2reg.slave_addr.addr0.de),
.d (hw2reg.slave_addr.addr0.d),
// to internal hardware
.qe (), .qe (),
.q (reg2hw.slave_data.q), .q (reg2hw.slave_addr.addr0.q),
.qs (slave_data_qs)
// to register interface (read)
.qs (slave_addr_addr0_qs)
); );
logic [2:0] addr_hit; // F[addr1]: 15:8
prim_subreg #(
.DW (8),
.SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_addr_addr1 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.slave_addr.addr1.de),
.d (hw2reg.slave_addr.addr1.d),
// to internal hardware
.qe (),
.q (reg2hw.slave_addr.addr1.q),
// to register interface (read)
.qs (slave_addr_addr1_qs)
);
// F[addr2]: 23:16
prim_subreg #(
.DW (8),
.SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_addr_addr2 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.slave_addr.addr2.de),
.d (hw2reg.slave_addr.addr2.d),
// to internal hardware
.qe (),
.q (reg2hw.slave_addr.addr2.q),
// to register interface (read)
.qs (slave_addr_addr2_qs)
);
// F[addr3]: 31:24
prim_subreg #(
.DW (8),
.SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_addr_addr3 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.slave_addr.addr3.de),
.d (hw2reg.slave_addr.addr3.d),
// to internal hardware
.qe (),
.q (reg2hw.slave_addr.addr3.q),
// to register interface (read)
.qs (slave_addr_addr3_qs)
);
// R[slave_wdata]: V(False)
// F[wdata0]: 7:0
prim_subreg #(
.DW (8),
.SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_wdata_wdata0 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.slave_wdata.wdata0.de),
.d (hw2reg.slave_wdata.wdata0.d),
// to internal hardware
.qe (),
.q (reg2hw.slave_wdata.wdata0.q),
// to register interface (read)
.qs (slave_wdata_wdata0_qs)
);
// F[wdata1]: 15:8
prim_subreg #(
.DW (8),
.SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_wdata_wdata1 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.slave_wdata.wdata1.de),
.d (hw2reg.slave_wdata.wdata1.d),
// to internal hardware
.qe (),
.q (reg2hw.slave_wdata.wdata1.q),
// to register interface (read)
.qs (slave_wdata_wdata1_qs)
);
// F[wdata2]: 23:16
prim_subreg #(
.DW (8),
.SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_wdata_wdata2 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.slave_wdata.wdata2.de),
.d (hw2reg.slave_wdata.wdata2.d),
// to internal hardware
.qe (),
.q (reg2hw.slave_wdata.wdata2.q),
// to register interface (read)
.qs (slave_wdata_wdata2_qs)
);
// F[wdata3]: 31:24
prim_subreg #(
.DW (8),
.SWACCESS("RO"),
.RESVAL (8'h0)
) u_slave_wdata_wdata3 (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (1'b0),
.wd ('0),
// from internal hardware
.de (hw2reg.slave_wdata.wdata3.de),
.d (hw2reg.slave_wdata.wdata3.d),
// to internal hardware
.qe (),
.q (reg2hw.slave_wdata.wdata3.q),
// to register interface (read)
.qs (slave_wdata_wdata3_qs)
);
// R[slave_rdata]: V(False)
prim_subreg #(
.DW (32),
.SWACCESS("RW"),
.RESVAL (32'h0)
) u_slave_rdata (
.clk_i (clk_i),
.rst_ni (rst_ni),
// from register interface
.we (slave_rdata_we),
.wd (slave_rdata_wd),
// from internal hardware
.de (1'b0),
.d ('0),
// to internal hardware
.qe (),
.q (reg2hw.slave_rdata.q),
// to register interface (read)
.qs (slave_rdata_qs)
);
logic [4:0] addr_hit;
always_comb begin always_comb begin
addr_hit = '0; addr_hit = '0;
addr_hit[0] = (reg_addr == I2C_CTRL_OFFSET); addr_hit[0] = (reg_addr == I2C_CTRL_OFFSET);
addr_hit[1] = (reg_addr == I2C_MASTER_DATA_OFFSET); addr_hit[1] = (reg_addr == I2C_MASTER_DATA_OFFSET);
addr_hit[2] = (reg_addr == I2C_SLAVE_DATA_OFFSET); addr_hit[2] = (reg_addr == I2C_SLAVE_ADDR_OFFSET);
addr_hit[3] = (reg_addr == I2C_SLAVE_WDATA_OFFSET);
addr_hit[4] = (reg_addr == I2C_SLAVE_RDATA_OFFSET);
end end
assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ;
@ -385,7 +675,9 @@ module i2c_reg_top (
wr_err = (reg_we & wr_err = (reg_we &
((addr_hit[0] & (|(I2C_PERMIT[0] & ~reg_be))) | ((addr_hit[0] & (|(I2C_PERMIT[0] & ~reg_be))) |
(addr_hit[1] & (|(I2C_PERMIT[1] & ~reg_be))) | (addr_hit[1] & (|(I2C_PERMIT[1] & ~reg_be))) |
(addr_hit[2] & (|(I2C_PERMIT[2] & ~reg_be))))); (addr_hit[2] & (|(I2C_PERMIT[2] & ~reg_be))) |
(addr_hit[3] & (|(I2C_PERMIT[3] & ~reg_be))) |
(addr_hit[4] & (|(I2C_PERMIT[4] & ~reg_be)))));
end end
assign ctrl_we = addr_hit[0] & reg_we & !reg_error; assign ctrl_we = addr_hit[0] & reg_we & !reg_error;
@ -400,6 +692,10 @@ module i2c_reg_top (
assign ctrl_write_wd = reg_wdata[4]; assign ctrl_write_wd = reg_wdata[4];
assign ctrl_slave_rdy_wd = reg_wdata[7];
assign ctrl_slave_addr_wd = reg_wdata[15:8];
assign ctrl_clk_div_wd = reg_wdata[31:16]; assign ctrl_clk_div_wd = reg_wdata[31:16];
assign master_data_we = addr_hit[1] & reg_we & !reg_error; assign master_data_we = addr_hit[1] & reg_we & !reg_error;
@ -408,7 +704,9 @@ module i2c_reg_top (
assign master_data_regreg_wd = reg_wdata[15:8]; assign master_data_regreg_wd = reg_wdata[15:8];
assign master_data_data_wd = reg_wdata[23:16]; assign master_data_data_wd = reg_wdata[23:16];
assign slave_data_re = addr_hit[2] & reg_re & !reg_error; assign slave_rdata_we = addr_hit[4] & reg_we & !reg_error;
assign slave_rdata_wd = reg_wdata[31:0];
// Read data return // Read data return
always_comb begin always_comb begin
@ -420,8 +718,10 @@ module i2c_reg_top (
reg_rdata_next[2] = ctrl_int_pending_qs; reg_rdata_next[2] = ctrl_int_pending_qs;
reg_rdata_next[3] = ctrl_mode_qs; reg_rdata_next[3] = ctrl_mode_qs;
reg_rdata_next[4] = ctrl_write_qs; reg_rdata_next[4] = ctrl_write_qs;
reg_rdata_next[5] = ctrl_ack_qs; reg_rdata_next[5] = ctrl_error_qs;
reg_rdata_next[6] = ctrl_error_qs; reg_rdata_next[6] = ctrl_slave_wr_qs;
reg_rdata_next[7] = ctrl_slave_rdy_qs;
reg_rdata_next[15:8] = ctrl_slave_addr_qs;
reg_rdata_next[31:16] = ctrl_clk_div_qs; reg_rdata_next[31:16] = ctrl_clk_div_qs;
end end
@ -432,7 +732,21 @@ module i2c_reg_top (
end end
addr_hit[2]: begin addr_hit[2]: begin
reg_rdata_next[7:0] = slave_data_qs; reg_rdata_next[7:0] = slave_addr_addr0_qs;
reg_rdata_next[15:8] = slave_addr_addr1_qs;
reg_rdata_next[23:16] = slave_addr_addr2_qs;
reg_rdata_next[31:24] = slave_addr_addr3_qs;
end
addr_hit[3]: begin
reg_rdata_next[7:0] = slave_wdata_wdata0_qs;
reg_rdata_next[15:8] = slave_wdata_wdata1_qs;
reg_rdata_next[23:16] = slave_wdata_wdata2_qs;
reg_rdata_next[31:24] = slave_wdata_wdata3_qs;
end
addr_hit[4]: begin
reg_rdata_next[31:0] = slave_rdata_qs;
end end
default: begin default: begin

270
rtl/perips/i2c/i2c_slave.sv Normal file
View File

@ -0,0 +1,270 @@
/*
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_slave (
input logic clk_i,
input logic rst_ni,
input logic enable_i,
input logic [7:0] slave_addr_i,
input logic [7:0] data_i,
output logic [7:0] addr_o,
output logic read_o,
output logic valid_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 scl_raise_edge, scl_fall_edge;
logic sda_raise_edge, sda_fall_edge;
logic sda, scl;
logic [5:0] state_d, state_q;
logic sda_oe_d, sda_oe_q;
logic scl_oe_d, scl_oe_q;
logic sda_d, sda_q;
logic scl_d, scl_q;
logic [7:0] data_d, data_q;
logic [7:0] addr_d, addr_q;
logic read_d, read_q;
logic send_ack_d, send_ack_q;
logic [3:0] scl_raise_edge_cnt_d, scl_raise_edge_cnt_q;
logic valid_d, valid_q;
logic op_read_d, op_read_q;
always_comb begin
state_d = state_q;
sda_oe_d = sda_oe_q;
sda_d = sda_q;
scl_oe_d = scl_oe_q;
scl_d = scl_q;
data_d = data_q;
addr_d = addr_q;
read_d = read_q;
send_ack_d = send_ack_q;
scl_raise_edge_cnt_d = scl_raise_edge_cnt_q;
valid_d = valid_q;
op_read_d = op_read_q;
if (!enable_i) begin
state_d = S_IDLE;
sda_oe_d = 1'b0;
sda_d = 1'b0;
scl_oe_d = 1'b0;
scl_d = 1'b0;
end else begin
case (state_q)
S_IDLE: begin
sda_oe_d = 1'b0;
sda_d = 1'b0;
valid_d = 1'b0;
read_d = 1'b0;
// START信号
if (scl_i && sda_fall_edge) begin
state_d = S_ADDR;
send_ack_d = 1'b0;
scl_raise_edge_cnt_d = '0;
op_read_d = 1'b0;
end
end
S_ADDR: begin
// SCL上升沿采数据
if (scl_raise_edge) begin
scl_raise_edge_cnt_d = scl_raise_edge_cnt_q + 1'b1;
if (scl_raise_edge_cnt_q < 4'd8) begin
data_d = {data_q[6:0], sda};
end
end
// 下降沿发ACK信号
if ((scl_raise_edge_cnt_q == 4'd8) && scl_fall_edge) begin
// 地址对得上则回ACK信号
if (slave_addr_i[7:1] == data_q[7:1]) begin
sda_oe_d = 1'b1;
sda_d = 1'b0;
if (data_q[0]) begin
read_d = 1'b1;
end
// 否则回到S_IDLE状态
end else begin
state_d = S_IDLE;
end
end
// 释放SDA
if ((scl_raise_edge_cnt_q == 4'd9) && scl_fall_edge) begin
sda_oe_d = 1'b0;
scl_raise_edge_cnt_d = '0;
// 读
if (data_q[0]) begin
state_d = S_DATA;
op_read_d = 1'b1;
sda_oe_d = 1'b1;
data_d = {data_i[6:0], 1'b1};
sda_d = data_i[7];
// 写
end else begin
state_d = S_REG;
op_read_d = 1'b0;
end
end
// 收到STOP信号
if (scl_i && sda_raise_edge) begin
state_d = S_IDLE;
end
end
S_REG: begin
// SCL上升沿采数据
if (scl_raise_edge) begin
scl_raise_edge_cnt_d = scl_raise_edge_cnt_q + 1'b1;
if (scl_raise_edge_cnt_q < 4'd8) begin
data_d = {data_q[6:0], sda};
end
end
// 下降沿发ACK信号
if ((scl_raise_edge_cnt_q == 4'd8) && scl_fall_edge) begin
sda_oe_d = 1'b1;
sda_d = 1'b0;
addr_d = data_q;
end
// 释放SDA
if ((scl_raise_edge_cnt_q == 4'd9) && scl_fall_edge) begin
sda_oe_d = 1'b0;
scl_raise_edge_cnt_d = '0;
state_d = S_DATA;
op_read_d = 1'b0;
end
// 收到STOP信号
if (scl_i && sda_raise_edge) begin
state_d = S_IDLE;
end
end
S_DATA: begin
if (scl_raise_edge) begin
scl_raise_edge_cnt_d = scl_raise_edge_cnt_q + 1'b1;
if ((!op_read_q) && (scl_raise_edge_cnt_q < 4'd8)) begin
data_d = {data_q[6:0], sda};
end
end else if (scl_fall_edge) begin
if (op_read_q && (scl_raise_edge_cnt_q < 4'd7)) begin
sda_oe_d = 1'b1;
data_d = {data_q[6:0], 1'b1};
sda_d = data_q[7];
end
end
// 下降沿发ACK信号
if ((scl_raise_edge_cnt_q == 4'd8) && scl_fall_edge) begin
sda_oe_d = 1'b1;
// 回NACK
if (op_read_q) begin
sda_d = 1'b1;
// 回ACK
end else begin
sda_d = 1'b0;
end
valid_d = 1'b1;
end
// 释放SDA
if ((scl_raise_edge_cnt_q == 4'd9) && scl_fall_edge) begin
sda_oe_d = 1'b0;
op_read_d = 1'b0;
end
// 收到STOP信号
if (scl_i && sda_raise_edge) begin
state_d = S_IDLE;
end
end
default: ;
endcase
end
end
always_ff @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_q <= S_IDLE;
sda_oe_q <= 1'b0;
sda_q <= 1'b0;
scl_oe_q <= 1'b0;
scl_q <= 1'b0;
data_q <= '0;
addr_q <= '0;
read_q <= 1'b0;
send_ack_q <= 1'b0;
scl_raise_edge_cnt_q <= '0;
valid_q <= 1'b0;
op_read_q <= 1'b0;
end else begin
state_q <= state_d;
sda_oe_q <= sda_oe_d;
sda_q <= sda_d;
scl_oe_q <= scl_oe_d;
scl_q <= scl_d;
data_q <= data_d;
addr_q <= addr_d;
read_q <= read_d;
send_ack_q <= send_ack_d;
scl_raise_edge_cnt_q <= scl_raise_edge_cnt_d;
valid_q <= valid_d;
op_read_q <= op_read_d;
end
end
assign scl_oe_o = scl_oe_q;
assign scl_o = scl_q;
assign sda_oe_o = sda_oe_q;
assign sda_o = sda_q;
assign data_o = data_q;
assign addr_o = addr_q;
assign read_o = read_q;
assign valid_o = valid_q;
// SCL信号沿检测
edge_detect scl_edge_detect (
.clk_i (clk_i),
.rst_ni (rst_ni),
.sig_i (scl_i),
.sig_o (scl),
.re_o (scl_raise_edge),
.fe_o (scl_fall_edge)
);
// SDA信号沿检测
edge_detect sda_edge_detect (
.clk_i (clk_i),
.rst_ni (rst_ni),
.sig_i (sda_i),
.sig_o (sda),
.re_o (sda_raise_edge),
.fe_o (sda_fall_edge)
);
endmodule