From e708eb6d4dcdcc7a4292845dbae98dc2637b5316 Mon Sep 17 00:00:00 2001 From: liangkangnan Date: Wed, 25 Aug 2021 17:51:35 +0800 Subject: [PATCH] rtl:perips:i2c: add i2c slave Signed-off-by: liangkangnan --- rtl.flist | 1 + rtl/perips/i2c/i2c.hjson | 72 +++++- rtl/perips/i2c/i2c_core.sv | 195 ++++++++++++++-- rtl/perips/i2c/i2c_reg_pkg.sv | 135 ++++++++--- rtl/perips/i2c/i2c_reg_top.sv | 408 ++++++++++++++++++++++++++++++---- rtl/perips/i2c/i2c_slave.sv | 270 ++++++++++++++++++++++ 6 files changed, 980 insertions(+), 101 deletions(-) create mode 100644 rtl/perips/i2c/i2c_slave.sv diff --git a/rtl.flist b/rtl.flist index 0cfca9f..c82fa25 100644 --- a/rtl.flist +++ b/rtl.flist @@ -58,6 +58,7 @@ ../rtl/perips/i2c/i2c_core.sv ../rtl/perips/i2c/i2c_top.sv ../rtl/perips/i2c/i2c_master.sv +../rtl/perips/i2c/i2c_slave.sv ../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv diff --git a/rtl/perips/i2c/i2c.hjson b/rtl/perips/i2c/i2c.hjson index 85a04b3..b606d1a 100644 --- a/rtl/perips/i2c/i2c.hjson +++ b/rtl/perips/i2c/i2c.hjson @@ -36,15 +36,23 @@ 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: "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", name: "CLK_DIV", desc: "I2C clock divider count", @@ -70,15 +78,59 @@ } ] } - { name: "SLAVE_DATA", - desc: "I2C slave received data register", + { name: "SLAVE_ADDR", + desc: "I2C slave read or write address register", swaccess: "ro", hwaccess: "hrw", - hwext: "true", - hwre: "true", fields: [ { 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", } ] } diff --git a/rtl/perips/i2c/i2c_core.sv b/rtl/perips/i2c/i2c_core.sv index 95ce126..3db8946 100644 --- a/rtl/perips/i2c/i2c_core.sv +++ b/rtl/perips/i2c/i2c_core.sv @@ -41,7 +41,6 @@ module i2c_core ( i2c_reg_pkg::i2c_hw2reg_t hw2reg; logic master_mode; - logic slave_mode; logic op_write; logic op_read; logic start; @@ -50,13 +49,36 @@ module i2c_core ( logic [7:0] master_address; logic [7:0] master_register; logic [7:0] master_data; - logic master_ready, master_ready_q; + logic master_ready, master_ready_re; logic master_start; logic master_error; 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 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; @@ -68,35 +90,152 @@ module i2c_core ( assign master_data = reg2hw.master_data.data.q; // 软件写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; - // master传输完成上升沿脉冲 - assign hw2reg.ctrl.start.de = (~master_ready_q) && master_ready; + assign hw2reg.ctrl.start.de = master_ready_re; // 传输完成产生中断pending 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 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 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; - 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; + edge_detect master_ready_edge_detect ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .sig_i (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 + // 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 ( .clk_i (clk_i), .rst_ni (rst_ni), @@ -111,11 +250,29 @@ module i2c_core ( .error_o (master_error), .data_o (master_read_data), .scl_i (scl_i), - .scl_o (scl_o), - .scl_oe_o (scl_oe_o), + .scl_o (master_scl), + .scl_oe_o (master_scl_oe), .sda_i (sda_i), - .sda_o (sda_o), - .sda_oe_o (sda_oe_o) + .sda_o (master_sda), + .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 ( diff --git a/rtl/perips/i2c/i2c_reg_pkg.sv b/rtl/perips/i2c/i2c_reg_pkg.sv index 74e32f3..0b27c56 100644 --- a/rtl/perips/i2c/i2c_reg_pkg.sv +++ b/rtl/perips/i2c/i2c_reg_pkg.sv @@ -7,7 +7,7 @@ package i2c_reg_pkg; // Address widths within the block - parameter int BlockAw = 4; + parameter int BlockAw = 5; //////////////////////////// // Typedefs for registers // @@ -37,11 +37,19 @@ package i2c_reg_pkg; struct packed { logic q; logic qe; - } ack; + } error; struct packed { logic q; 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 { logic [15:0] q; logic qe; @@ -61,9 +69,38 @@ package i2c_reg_pkg; } i2c_reg2hw_master_data_reg_t; typedef struct packed { - logic [7:0] q; - logic re; - } i2c_reg2hw_slave_data_reg_t; + struct packed { + logic [7:0] q; + } addr0; + 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 { struct packed { @@ -89,11 +126,19 @@ package i2c_reg_pkg; struct packed { logic d; logic de; - } ack; + } error; struct packed { logic d; 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 { logic [15:0] d; logic de; @@ -116,43 +161,83 @@ package i2c_reg_pkg; } i2c_hw2reg_master_data_reg_t; typedef struct packed { - logic [7:0] d; - } i2c_hw2reg_slave_data_reg_t; + struct packed { + logic [7:0] d; + 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 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_ctrl_reg_t ctrl; // [161:120] + i2c_reg2hw_master_data_reg_t master_data; // [119:96] + 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; // 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_ctrl_reg_t ctrl; // [140:99] + i2c_hw2reg_master_data_reg_t master_data; // [98:72] + i2c_hw2reg_slave_addr_reg_t slave_addr; // [71:36] + i2c_hw2reg_slave_wdata_reg_t slave_wdata; // [35: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; + parameter logic [BlockAw-1:0] I2C_CTRL_OFFSET = 5'h0; + parameter logic [BlockAw-1:0] I2C_MASTER_DATA_OFFSET = 5'h4; + parameter logic [BlockAw-1:0] I2C_SLAVE_ADDR_OFFSET = 5'h8; + parameter logic [BlockAw-1:0] I2C_SLAVE_WDATA_OFFSET = 5'hc; + parameter logic [BlockAw-1:0] I2C_SLAVE_RDATA_OFFSET = 5'h10; // Register index typedef enum int { I2C_CTRL, I2C_MASTER_DATA, - I2C_SLAVE_DATA + I2C_SLAVE_ADDR, + I2C_SLAVE_WDATA, + I2C_SLAVE_RDATA } i2c_id_e; // 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'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 diff --git a/rtl/perips/i2c/i2c_reg_top.sv b/rtl/perips/i2c/i2c_reg_top.sv index 7a57cbe..41d6de4 100644 --- a/rtl/perips/i2c/i2c_reg_top.sv +++ b/rtl/perips/i2c/i2c_reg_top.sv @@ -23,7 +23,7 @@ module i2c_reg_top ( import i2c_reg_pkg::* ; - localparam int AW = 4; + localparam int AW = 5; localparam int DW = 32; localparam int DBW = DW/8; // Byte Width @@ -49,8 +49,12 @@ module i2c_reg_top ( logic ctrl_mode_wd; logic ctrl_write_qs; logic ctrl_write_wd; - logic ctrl_ack_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_wd; logic master_data_we; @@ -60,8 +64,17 @@ module i2c_reg_top ( 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; + logic [7:0] slave_addr_addr0_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 // R[ctrl]: V(False) @@ -196,33 +209,7 @@ module i2c_reg_top ( ); - // 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 + // F[error]: 5:5 prim_subreg #( .DW (1), .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 prim_subreg #( .DW (16), @@ -354,28 +419,253 @@ module i2c_reg_top ( ); - // R[slave_data]: V(True) + // R[slave_addr]: V(False) - prim_subreg_ext #( - .DW (8) - ) u_slave_data ( - .re (slave_data_re), + // F[addr0]: 7:0 + prim_subreg #( + .DW (8), + .SWACCESS("RO"), + .RESVAL (8'h0) + ) u_slave_addr_addr0 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface .we (1'b0), .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 (), - .q (reg2hw.slave_data.q), - .qs (slave_data_qs) + .q (reg2hw.slave_addr.addr0.q), + + // 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 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); + 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 assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; @@ -385,7 +675,9 @@ module i2c_reg_top ( 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))))); + (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 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_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 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_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 always_comb begin @@ -420,8 +718,10 @@ module i2c_reg_top ( 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[5] = 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; end @@ -432,7 +732,21 @@ module i2c_reg_top ( end 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 default: begin diff --git a/rtl/perips/i2c/i2c_slave.sv b/rtl/perips/i2c/i2c_slave.sv new file mode 100644 index 0000000..f9b6352 --- /dev/null +++ b/rtl/perips/i2c/i2c_slave.sv @@ -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