| /* |
| ///////////////////////////////////////////////////////////////////// |
| //// //// |
| //// Copyright (C) 2001 Richard Herveille //// |
| //// richard@asics.ws //// |
| //// //// |
| //// This source file may be used and distributed without //// |
| //// restriction provided that this copyright statement is not //// |
| //// removed from the file and that any derivative work contains //// |
| //// the original copyright notice and the associated disclaimer.//// |
| //// //// |
| //// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY //// |
| //// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED //// |
| //// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS //// |
| //// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR //// |
| //// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, //// |
| //// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //// |
| //// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE //// |
| //// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR //// |
| //// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF //// |
| //// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //// |
| //// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT //// |
| //// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE //// |
| //// POSSIBILITY OF SUCH DAMAGE. //// |
| //// //// |
| ///////////////////////////////////////////////////////////////////// |
| */ |
| /* |
| i2c master controller. |
| Based on the OpenCores i2c master controller by Richard Herveille |
| Updated to: |
| (1) remove unnecessary latches and |
| (2) enable asynchronous reading |
| Author: Mohamed Shalan |
| */ |
| |
| |
| `timescale 1ns/1ps |
| `default_nettype none |
| |
| |
| // bitcontroller states |
| `define I2C_CMD_NOP 4'b0000 |
| `define I2C_CMD_START 4'b0001 |
| `define I2C_CMD_STOP 4'b0010 |
| `define I2C_CMD_WRITE 4'b0100 |
| `define I2C_CMD_READ 4'b1000 |
| |
| |
| module i2c_master_bit_ctrl ( |
| input clk, // system clock |
| input rst, // asynchronous active high reset |
| input ena, // core enable signal |
| |
| input [15:0] clk_cnt, // clock prescale value |
| |
| input [ 3:0] cmd, // command (from byte controller) |
| output reg cmd_ack, // command complete acknowledge |
| output reg busy, // i2c bus busy |
| output reg al, // i2c bus arbitration lost |
| |
| input din, |
| output reg dout, |
| |
| input scl_i, // i2c clock line input |
| output scl_o, // i2c clock line output |
| output reg scl_oen, // i2c clock line output enable (active low) |
| input sda_i, // i2c data line input |
| output sda_o, // i2c data line output |
| output reg sda_oen // i2c data line output enable (active low) |
| ); |
| |
| // |
| // variable declarations |
| // |
| |
| reg [ 1:0] cSCL, cSDA; // capture SCL and SDA |
| reg [ 2:0] fSCL, fSDA; // SCL and SDA filter inputs |
| reg sSCL, sSDA; // filtered and synchronized SCL and SDA inputs |
| reg dSCL, dSDA; // delayed versions of sSCL and sSDA |
| reg dscl_oen; // delayed scl_oen |
| reg sda_chk; // check SDA output (Multi-master arbitration) |
| reg clk_en; // clock generation signals |
| reg slave_wait; // slave inserts wait states |
| reg [15:0] cnt; // clock divider counter (synthesis) |
| reg [13:0] filter_cnt; // clock divider for filter |
| |
| |
| // state machine variable |
| reg [17:0] c_state; |
| |
| // |
| // module body |
| // |
| |
| // whenever the slave is not ready it can delay the cycle by pulling SCL low |
| // delay scl_oen |
| always @(posedge clk) |
| dscl_oen <= #1 scl_oen; |
| |
| // slave_wait is asserted when master wants to drive SCL high, but the slave pulls it low |
| // slave_wait remains asserted until the slave releases SCL |
| always @(posedge clk or posedge rst) |
| if (rst) slave_wait <= 1'b0; |
| else slave_wait <= (scl_oen & ~dscl_oen & ~sSCL) | (slave_wait & ~sSCL); |
| |
| // master drives SCL high, but another master pulls it low |
| // master start counting down its low cycle now (clock synchronization) |
| wire scl_sync = dSCL & ~sSCL & scl_oen; |
| |
| |
| // generate clk enable signal |
| always @(posedge clk or posedge rst) |
| if (rst) |
| begin |
| cnt <= #1 16'h0; |
| clk_en <= #1 1'b1; |
| end |
| else if ( ~|cnt || !ena || scl_sync) |
| begin |
| cnt <= #1 clk_cnt; |
| clk_en <= #1 1'b1; |
| end |
| else if (slave_wait) |
| begin |
| cnt <= #1 cnt; |
| clk_en <= #1 1'b0; |
| end |
| else |
| begin |
| cnt <= #1 cnt - 16'h1; |
| clk_en <= #1 1'b0; |
| end |
| |
| |
| // generate bus status controller |
| |
| // capture SDA and SCL |
| // reduce metastability risk |
| always @(posedge clk or posedge rst) |
| if (rst) |
| begin |
| cSCL <= #1 2'b00; |
| cSDA <= #1 2'b00; |
| end |
| else |
| begin |
| cSCL <= {cSCL[0],scl_i}; |
| cSDA <= {cSDA[0],sda_i}; |
| end |
| |
| |
| // filter SCL and SDA signals; (attempt to) remove glitches |
| always @(posedge clk or posedge rst) |
| if (rst) filter_cnt <= 14'h0; |
| else if (!ena ) filter_cnt <= 14'h0; |
| else if (~|filter_cnt) filter_cnt <= clk_cnt >> 2; //16x I2C bus frequency |
| else filter_cnt <= filter_cnt -1; |
| |
| |
| always @(posedge clk or posedge rst) |
| if (rst) |
| begin |
| fSCL <= 3'b111; |
| fSDA <= 3'b111; |
| end |
| else if (~|filter_cnt) |
| begin |
| fSCL <= {fSCL[1:0],cSCL[1]}; |
| fSDA <= {fSDA[1:0],cSDA[1]}; |
| end |
| |
| |
| // generate filtered SCL and SDA signals |
| always @(posedge clk or posedge rst) |
| if (rst) |
| begin |
| sSCL <= #1 1'b1; |
| sSDA <= #1 1'b1; |
| |
| dSCL <= #1 1'b1; |
| dSDA <= #1 1'b1; |
| end |
| else |
| begin |
| sSCL <= #1 &fSCL[2:1] | &fSCL[1:0] | (fSCL[2] & fSCL[0]); |
| sSDA <= #1 &fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0]); |
| |
| dSCL <= #1 sSCL; |
| dSDA <= #1 sSDA; |
| end |
| |
| // detect start condition => detect falling edge on SDA while SCL is high |
| // detect stop condition => detect rising edge on SDA while SCL is high |
| reg sta_condition; |
| reg sto_condition; |
| always @(posedge clk or posedge rst) |
| if (rst) |
| begin |
| sta_condition <= #1 1'b0; |
| sto_condition <= #1 1'b0; |
| end |
| else |
| begin |
| sta_condition <= #1 ~sSDA & dSDA & sSCL; |
| sto_condition <= #1 sSDA & ~dSDA & sSCL; |
| end |
| |
| |
| // generate i2c bus busy signal |
| always @(posedge clk or posedge rst) |
| if (rst ) busy <= #1 1'b0; |
| else busy <= #1 (sta_condition | busy) & ~sto_condition; |
| |
| |
| // generate arbitration lost signal |
| // aribitration lost when: |
| // 1) master drives SDA high, but the i2c bus is low |
| // 2) stop detected while not requested |
| reg cmd_stop; |
| always @(posedge clk or posedge rst) |
| if (rst) |
| cmd_stop <= #1 1'b0; |
| else if (clk_en) |
| cmd_stop <= #1 cmd == `I2C_CMD_STOP; |
| |
| always @(posedge clk or posedge rst) |
| if (rst) |
| al <= #1 1'b0; |
| else |
| al <= #1 (sda_chk & ~sSDA & sda_oen) | (|c_state & sto_condition & ~cmd_stop); |
| |
| |
| // generate dout signal (store SDA on rising edge of SCL) |
| always @(posedge clk) |
| if (sSCL & ~dSCL) dout <= #1 sSDA; |
| |
| |
| // generate statemachine |
| |
| // nxt_state decoder |
| parameter [17:0] idle = 18'b0_0000_0000_0000_0000; |
| parameter [17:0] start_a = 18'b0_0000_0000_0000_0001; |
| parameter [17:0] start_b = 18'b0_0000_0000_0000_0010; |
| parameter [17:0] start_c = 18'b0_0000_0000_0000_0100; |
| parameter [17:0] start_d = 18'b0_0000_0000_0000_1000; |
| parameter [17:0] start_e = 18'b0_0000_0000_0001_0000; |
| parameter [17:0] stop_a = 18'b0_0000_0000_0010_0000; |
| parameter [17:0] stop_b = 18'b0_0000_0000_0100_0000; |
| parameter [17:0] stop_c = 18'b0_0000_0000_1000_0000; |
| parameter [17:0] stop_d = 18'b0_0000_0001_0000_0000; |
| parameter [17:0] rd_a = 18'b0_0000_0010_0000_0000; |
| parameter [17:0] rd_b = 18'b0_0000_0100_0000_0000; |
| parameter [17:0] rd_c = 18'b0_0000_1000_0000_0000; |
| parameter [17:0] rd_d = 18'b0_0001_0000_0000_0000; |
| parameter [17:0] wr_a = 18'b0_0010_0000_0000_0000; |
| parameter [17:0] wr_b = 18'b0_0100_0000_0000_0000; |
| parameter [17:0] wr_c = 18'b0_1000_0000_0000_0000; |
| parameter [17:0] wr_d = 18'b1_0000_0000_0000_0000; |
| |
| always @(posedge clk or posedge rst) |
| if (rst) |
| begin |
| c_state <= #1 idle; |
| cmd_ack <= #1 1'b0; |
| scl_oen <= #1 1'b1; |
| sda_oen <= #1 1'b1; |
| sda_chk <= #1 1'b0; |
| end |
| else if (al) |
| begin |
| c_state <= #1 idle; |
| cmd_ack <= #1 1'b0; |
| scl_oen <= #1 1'b1; |
| sda_oen <= #1 1'b1; |
| sda_chk <= #1 1'b0; |
| end |
| else |
| begin |
| cmd_ack <= #1 1'b0; // default no command acknowledge + assert cmd_ack only 1clk cycle |
| |
| if (clk_en) |
| case (c_state) |
| // idle state |
| idle: |
| begin |
| case (cmd) |
| `I2C_CMD_START: c_state <= #1 start_a; |
| `I2C_CMD_STOP: c_state <= #1 stop_a; |
| `I2C_CMD_WRITE: c_state <= #1 wr_a; |
| `I2C_CMD_READ: c_state <= #1 rd_a; |
| default: c_state <= #1 idle; |
| endcase |
| |
| scl_oen <= #1 scl_oen; // keep SCL in same state |
| sda_oen <= #1 sda_oen; // keep SDA in same state |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| // start |
| start_a: |
| begin |
| c_state <= #1 start_b; |
| scl_oen <= #1 scl_oen; // keep SCL in same state |
| sda_oen <= #1 1'b1; // set SDA high |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| start_b: |
| begin |
| c_state <= #1 start_c; |
| scl_oen <= #1 1'b1; // set SCL high |
| sda_oen <= #1 1'b1; // keep SDA high |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| start_c: |
| begin |
| c_state <= #1 start_d; |
| scl_oen <= #1 1'b1; // keep SCL high |
| sda_oen <= #1 1'b0; // set SDA low |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| start_d: |
| begin |
| c_state <= #1 start_e; |
| scl_oen <= #1 1'b1; // keep SCL high |
| sda_oen <= #1 1'b0; // keep SDA low |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| start_e: |
| begin |
| c_state <= #1 idle; |
| cmd_ack <= #1 1'b1; |
| scl_oen <= #1 1'b0; // set SCL low |
| sda_oen <= #1 1'b0; // keep SDA low |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| // stop |
| stop_a: |
| begin |
| c_state <= #1 stop_b; |
| scl_oen <= #1 1'b0; // keep SCL low |
| sda_oen <= #1 1'b0; // set SDA low |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| stop_b: |
| begin |
| c_state <= #1 stop_c; |
| scl_oen <= #1 1'b1; // set SCL high |
| sda_oen <= #1 1'b0; // keep SDA low |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| stop_c: |
| begin |
| c_state <= #1 stop_d; |
| scl_oen <= #1 1'b1; // keep SCL high |
| sda_oen <= #1 1'b0; // keep SDA low |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| stop_d: |
| begin |
| c_state <= #1 idle; |
| cmd_ack <= #1 1'b1; |
| scl_oen <= #1 1'b1; // keep SCL high |
| sda_oen <= #1 1'b1; // set SDA high |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| // read |
| rd_a: |
| begin |
| c_state <= #1 rd_b; |
| scl_oen <= #1 1'b0; // keep SCL low |
| sda_oen <= #1 1'b1; // tri-state SDA |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| rd_b: |
| begin |
| c_state <= #1 rd_c; |
| scl_oen <= #1 1'b1; // set SCL high |
| sda_oen <= #1 1'b1; // keep SDA tri-stated |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| rd_c: |
| begin |
| c_state <= #1 rd_d; |
| scl_oen <= #1 1'b1; // keep SCL high |
| sda_oen <= #1 1'b1; // keep SDA tri-stated |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| rd_d: |
| begin |
| c_state <= #1 idle; |
| cmd_ack <= #1 1'b1; |
| scl_oen <= #1 1'b0; // set SCL low |
| sda_oen <= #1 1'b1; // keep SDA tri-stated |
| sda_chk <= #1 1'b0; // don't check SDA output |
| end |
| |
| // write |
| wr_a: |
| begin |
| c_state <= #1 wr_b; |
| scl_oen <= #1 1'b0; // keep SCL low |
| sda_oen <= #1 din; // set SDA |
| sda_chk <= #1 1'b0; // don't check SDA output (SCL low) |
| end |
| |
| wr_b: |
| begin |
| c_state <= #1 wr_c; |
| scl_oen <= #1 1'b1; // set SCL high |
| sda_oen <= #1 din; // keep SDA |
| sda_chk <= #1 1'b0; // don't check SDA output yet |
| // allow some time for SDA and SCL to settle |
| end |
| |
| wr_c: |
| begin |
| c_state <= #1 wr_d; |
| scl_oen <= #1 1'b1; // keep SCL high |
| sda_oen <= #1 din; |
| sda_chk <= #1 1'b1; // check SDA output |
| end |
| |
| wr_d: |
| begin |
| c_state <= #1 idle; |
| cmd_ack <= #1 1'b1; |
| scl_oen <= #1 1'b0; // set SCL low |
| sda_oen <= #1 din; |
| sda_chk <= #1 1'b0; // don't check SDA output (SCL low) |
| end |
| |
| endcase |
| end |
| |
| |
| // assign scl and sda output (always gnd) |
| assign scl_o = 1'b0; |
| assign sda_o = 1'b0; |
| |
| endmodule |
| |
| module i2c_master_byte_ctrl ( |
| clk, rst, ena, clk_cnt, start, stop, read, write, ack_in, din, |
| cmd_ack, ack_out, dout, i2c_busy, i2c_al, scl_i, scl_o, scl_oen, sda_i, sda_o, sda_oen ); |
| |
| // |
| // inputs & outputs |
| // |
| input clk; // master clock |
| input rst; // asynchronous active high reset |
| input ena; // core enable signal |
| |
| input [15:0] clk_cnt; // 4x SCL |
| |
| // control inputs |
| input start; |
| input stop; |
| input read; |
| input write; |
| input ack_in; |
| input [7:0] din; |
| |
| // status outputs |
| output cmd_ack; |
| reg cmd_ack; |
| output ack_out; |
| reg ack_out; |
| output i2c_busy; |
| output i2c_al; |
| output [7:0] dout; |
| |
| // I2C signals |
| input scl_i; |
| output scl_o; |
| output scl_oen; |
| input sda_i; |
| output sda_o; |
| output sda_oen; |
| |
| |
| // |
| // Variable declarations |
| // |
| |
| // statemachine |
| parameter [4:0] ST_IDLE = 5'b0_0000; |
| parameter [4:0] ST_START = 5'b0_0001; |
| parameter [4:0] ST_READ = 5'b0_0010; |
| parameter [4:0] ST_WRITE = 5'b0_0100; |
| parameter [4:0] ST_ACK = 5'b0_1000; |
| parameter [4:0] ST_STOP = 5'b1_0000; |
| |
| // signals for bit_controller |
| reg [3:0] core_cmd; |
| reg core_txd; |
| wire core_ack, core_rxd; |
| |
| // signals for shift register |
| reg [7:0] sr; //8bit shift register |
| reg shift, ld; |
| |
| // signals for state machine |
| wire go; |
| reg [2:0] dcnt; |
| wire cnt_done; |
| |
| // |
| // Module body |
| // |
| |
| // hookup bit_controller |
| i2c_master_bit_ctrl bit_controller ( |
| .clk ( clk ), |
| .rst ( rst ), |
| .ena ( ena ), |
| .clk_cnt ( clk_cnt ), |
| .cmd ( core_cmd ), |
| .cmd_ack ( core_ack ), |
| .busy ( i2c_busy ), |
| .al ( i2c_al ), |
| .din ( core_txd ), |
| .dout ( core_rxd ), |
| .scl_i ( scl_i ), |
| .scl_o ( scl_o ), |
| .scl_oen ( scl_oen ), |
| .sda_i ( sda_i ), |
| .sda_o ( sda_o ), |
| .sda_oen ( sda_oen ) |
| ); |
| |
| // generate go-signal |
| assign go = (read | write | stop) & ~cmd_ack; |
| |
| // assign dout output to shift-register |
| assign dout = sr; |
| |
| // generate shift register |
| always @(posedge clk or posedge rst) |
| if (rst) |
| sr <= #1 8'h0; |
| else if (ld) |
| sr <= #1 din; |
| else if (shift) |
| sr <= #1 {sr[6:0], core_rxd}; |
| |
| // generate counter |
| always @(posedge clk or posedge rst) |
| if (rst) |
| dcnt <= #1 3'h0; |
| else if (ld) |
| dcnt <= #1 3'h7; |
| else if (shift) |
| dcnt <= #1 dcnt - 3'h1; |
| |
| assign cnt_done = ~(|dcnt); |
| |
| // |
| // state machine |
| // |
| reg [4:0] c_state; |
| |
| always @(posedge clk or posedge rst) |
| if (rst) |
| begin |
| core_cmd <= #1 `I2C_CMD_NOP; |
| core_txd <= #1 1'b0; |
| shift <= #1 1'b0; |
| ld <= #1 1'b0; |
| cmd_ack <= #1 1'b0; |
| c_state <= #1 ST_IDLE; |
| ack_out <= #1 1'b0; |
| end |
| else if (i2c_al) |
| begin |
| core_cmd <= #1 `I2C_CMD_NOP; |
| core_txd <= #1 1'b0; |
| shift <= #1 1'b0; |
| ld <= #1 1'b0; |
| cmd_ack <= #1 1'b0; |
| c_state <= #1 ST_IDLE; |
| ack_out <= #1 1'b0; |
| end |
| else |
| begin |
| // initially reset all signals |
| core_txd <= #1 sr[7]; |
| shift <= #1 1'b0; |
| ld <= #1 1'b0; |
| cmd_ack <= #1 1'b0; |
| |
| case (c_state) // synopsys full_case parallel_case |
| ST_IDLE: |
| if (go) |
| begin |
| if (start) |
| begin |
| c_state <= #1 ST_START; |
| core_cmd <= #1 `I2C_CMD_START; |
| end |
| else if (read) |
| begin |
| c_state <= #1 ST_READ; |
| core_cmd <= #1 `I2C_CMD_READ; |
| end |
| else if (write) |
| begin |
| c_state <= #1 ST_WRITE; |
| core_cmd <= #1 `I2C_CMD_WRITE; |
| end |
| else // stop |
| begin |
| c_state <= #1 ST_STOP; |
| core_cmd <= #1 `I2C_CMD_STOP; |
| end |
| |
| ld <= #1 1'b1; |
| end |
| |
| ST_START: |
| if (core_ack) |
| begin |
| if (read) |
| begin |
| c_state <= #1 ST_READ; |
| core_cmd <= #1 `I2C_CMD_READ; |
| end |
| else |
| begin |
| c_state <= #1 ST_WRITE; |
| core_cmd <= #1 `I2C_CMD_WRITE; |
| end |
| |
| ld <= #1 1'b1; |
| end |
| |
| ST_WRITE: |
| if (core_ack) |
| if (cnt_done) |
| begin |
| c_state <= #1 ST_ACK; |
| core_cmd <= #1 `I2C_CMD_READ; |
| end |
| else |
| begin |
| c_state <= #1 ST_WRITE; // stay in same state |
| core_cmd <= #1 `I2C_CMD_WRITE; // write next bit |
| shift <= #1 1'b1; |
| end |
| |
| ST_READ: |
| if (core_ack) |
| begin |
| if (cnt_done) |
| begin |
| c_state <= #1 ST_ACK; |
| core_cmd <= #1 `I2C_CMD_WRITE; |
| end |
| else |
| begin |
| c_state <= #1 ST_READ; // stay in same state |
| core_cmd <= #1 `I2C_CMD_READ; // read next bit |
| end |
| |
| shift <= #1 1'b1; |
| core_txd <= #1 ack_in; |
| end |
| |
| ST_ACK: |
| if (core_ack) |
| begin |
| if (stop) |
| begin |
| c_state <= #1 ST_STOP; |
| core_cmd <= #1 `I2C_CMD_STOP; |
| end |
| else |
| begin |
| c_state <= #1 ST_IDLE; |
| core_cmd <= #1 `I2C_CMD_NOP; |
| |
| // generate command acknowledge signal |
| cmd_ack <= #1 1'b1; |
| end |
| |
| // assign ack_out output to bit_controller_rxd (contains last received bit) |
| ack_out <= #1 core_rxd; |
| |
| core_txd <= #1 1'b1; |
| end |
| else |
| core_txd <= #1 ack_in; |
| |
| ST_STOP: |
| if (core_ack) |
| begin |
| c_state <= #1 ST_IDLE; |
| core_cmd <= #1 `I2C_CMD_NOP; |
| |
| // generate command acknowledge signal |
| cmd_ack <= #1 1'b1; |
| end |
| |
| endcase |
| end |
| endmodule |
| |
| |
| module i2c_master #( |
| parameter base_addr = 6'h0 |
| ) |
| ( |
| // |
| input wire sys_clk, |
| input wire sys_rst, |
| // |
| input wire [5:3] io_a, |
| input wire [7:0] io_di, |
| output reg [7:0] io_do, |
| input wire io_re, |
| input wire io_we, |
| // |
| output reg i2c_irq, |
| // |
| input wire scl_i, // SCL-line input |
| output wire scl_o, // SCL-line output (always 1'b0) |
| output wire scl_oen_o, // SCL-line output enable (active low) |
| input wire sda_i, // SDA-line input |
| output wire sda_o, // SDA-line output (always 1'b0) |
| output wire sda_oen_o // SDA-line output enable (active low) |
| ); |
| |
| // register address |
| parameter [2:0] PRE_LO_ADDR = 3'h0; |
| parameter [2:0] PRE_HI_ADDR = 3'h1; |
| parameter [2:0] CTRL_ADDR = 3'h2; |
| parameter [2:0] TXR_ADDR = 3'h3; |
| parameter [2:0] RXR_ADDR = 3'h4; |
| parameter [2:0] CR_ADDR = 3'h5; |
| parameter [2:0] SR_ADDR = 3'h6; |
| |
| wire csr_selected = (io_a == PRE_LO_ADDR) | (io_a == PRE_HI_ADDR) | |
| (io_a == CTRL_ADDR) | (io_a == RXR_ADDR) | |
| (io_a == SR_ADDR) | (io_a == TXR_ADDR) | |
| (io_a == CR_ADDR); |
| |
| // |
| // variable declarations |
| // |
| |
| // registers |
| reg [15:0] prer; // clock prescale register |
| reg [ 7:0] ctr; // control register |
| reg [ 7:0] txr; // transmit register |
| wire [ 7:0] rxr; // receive register |
| reg [ 7:0] cr; // command register |
| wire [ 7:0] sr; // status register |
| |
| // done signal: command completed, clear command register |
| wire done; |
| |
| // core enable signal |
| wire core_en; |
| wire ien; |
| |
| // status register signals |
| wire irxack; |
| reg rxack; // received aknowledge from slave |
| reg tip; // transfer in progress |
| reg irq_flag; // interrupt pending flag |
| wire i2c_busy; // bus busy (start signal detected) |
| wire i2c_al; // i2c bus arbitration lost |
| reg al; // status register arbitration lost bit |
| |
| // |
| // module body |
| // |
| |
| // assign io_do |
| always @* |
| begin |
| io_do = 8'd00; |
| if(io_re & csr_selected) |
| case (io_a) |
| PRE_LO_ADDR: io_do = #1 prer[ 7:0]; |
| PRE_HI_ADDR: io_do = #1 prer[15:8]; |
| CTRL_ADDR: io_do = #1 ctr; |
| RXR_ADDR: io_do = #1 rxr; |
| SR_ADDR: io_do = #1 sr; |
| TXR_ADDR: io_do = #1 txr; |
| CR_ADDR: io_do = #1 cr; |
| default: io_do = 8'hff; // reserved |
| endcase |
| end |
| |
| /* |
| always @(posedge sys_clk) |
| begin |
| //io_do <= 8'd00; |
| if(io_re & csr_selected) |
| case (io_a) |
| prer_low_addr: io_do <= #1 prer[ 7:0]; |
| prer_high_addr: io_do <= #1 prer[15:8]; |
| ctr_addr: io_do <= #1 ctr; |
| rxr_addr: io_do <= #1 rxr; |
| sr_addr: io_do <= #1 sr; |
| txr_addr: io_do <= #1 txr; |
| cr_addr: io_do <= #1 cr; |
| default: ; // reserved |
| endcase |
| end |
| */ |
| // generate registers |
| always @(posedge sys_clk or posedge sys_rst) |
| if (sys_rst) |
| begin |
| prer <= #1 16'hffff; |
| ctr <= #1 8'h0; |
| txr <= #1 8'h0; |
| end |
| else |
| if (io_we & csr_selected) |
| case (io_a) |
| PRE_LO_ADDR : prer [ 7:0] <= #1 io_di; |
| PRE_HI_ADDR : prer [15:8] <= #1 io_di; |
| CTRL_ADDR : ctr <= #1 io_di; |
| TXR_ADDR : txr <= #1 io_di; |
| default: ; |
| endcase |
| |
| // generate command register (special case) |
| always @(posedge sys_clk or posedge sys_rst) |
| if (sys_rst) |
| cr <= #1 8'h0; |
| else if (io_we & (io_a == CR_ADDR)) |
| begin |
| if (core_en) |
| cr <= #1 io_di; |
| end |
| else |
| begin |
| if (done | i2c_al) |
| cr[7:4] <= #1 4'h0; // clear command bits when done |
| // or when aribitration lost |
| cr[2:1] <= #1 2'b0; // reserved bits |
| cr[0] <= #1 1'b0; // clear IRQ_ACK bit |
| end |
| |
| |
| // decode command register |
| wire sta = cr[7]; |
| wire sto = cr[6]; |
| wire rd = cr[5]; |
| wire wr = cr[4]; |
| wire ack = cr[3]; |
| wire iack = cr[0]; |
| |
| // decode control register |
| assign core_en = ctr[7]; |
| assign ien = ctr[6]; |
| |
| // hookup byte controller block |
| i2c_master_byte_ctrl byte_controller ( |
| .clk ( sys_clk ), |
| .rst ( sys_rst ), |
| .ena ( core_en ), |
| .clk_cnt ( prer ), |
| .start ( sta ), |
| .stop ( sto ), |
| .read ( rd ), |
| .write ( wr ), |
| .ack_in ( ack ), |
| .din ( txr ), |
| .cmd_ack ( done ), |
| .ack_out ( irxack ), |
| .dout ( rxr ), |
| .i2c_busy ( i2c_busy ), |
| .i2c_al ( i2c_al ), |
| .scl_i ( scl_i ), |
| .scl_o ( scl_o ), |
| .scl_oen ( scl_oen_o ), |
| .sda_i ( sda_i ), |
| .sda_o ( sda_o ), |
| .sda_oen ( sda_oen_o ) |
| ); |
| |
| // status register block + interrupt request signal |
| always @(posedge sys_clk or posedge sys_rst) |
| if (sys_rst) |
| begin |
| al <= #1 1'b0; |
| rxack <= #1 1'b0; |
| tip <= #1 1'b0; |
| irq_flag <= #1 1'b0; |
| end |
| else |
| begin |
| al <= #1 i2c_al | (al & ~sta); |
| rxack <= #1 irxack; |
| tip <= #1 (rd | wr); |
| irq_flag <= #1 (done | i2c_al /*| irq_flag*/) & ~iack; // interrupt request flag is always generated |
| end |
| |
| // generate interrupt request signals |
| always @(posedge sys_clk or posedge sys_rst) |
| if (sys_rst) |
| i2c_irq <= #1 1'b0; |
| else |
| i2c_irq <= #1 irq_flag && ien; // interrupt signal is only generated when IEN (interrupt enable bit is set) |
| |
| // assign status register bits |
| assign sr[7] = rxack; |
| assign sr[6] = i2c_busy; |
| assign sr[5] = al; |
| assign sr[4:2] = 3'h0; // reserved |
| assign sr[1] = tip; |
| assign sr[0] = irq_flag; |
| |
| endmodule |