// File:    i2c.v
// Author:  Gaspar Sinai
// Version: 2010-04-22
//
// A simple i2c slave implementation
//
module i2c (sda_io, scl_i, data_i, data_o);

inout sda_io;
input scl_i;

input [7:0] data_i;
output [7:0] data_o;


reg [7:0] data_i_reg;

reg [7:0] data_o_reg;

reg sda_io_reg;
initial sda_io_reg = 1'bz;

assign data_o = data_o_reg;
assign sda_io = sda_io_reg;

parameter ADDRESS = 7'h21; // our i2c address.

reg [7:0] data;

reg [8:0] index;
initial index = 9'b0;

reg header;
initial header = 1'b0;

reg sel;
initial sel = 1'b0;

reg write;
initial write = 1'b0;

reg start_scl;
initial start_scl = 1'b0;

reg start_neg;
initial start_neg = 1'b0;


// we detect start, dont detect stop
always @(negedge sda_io)

    if (scl_i) begin
        start_neg <= ~start_scl; // start
    end

// read on risign edge
always @(posedge scl_i) begin

    if (start_scl != start_neg) begin
        start_scl <= start_neg;
        sel <= 0;
        index <= 9'b000000010; // there is no incrememnt here, that's why
        header <= 1'b1;
        write <= 0;
        data[0] <= sda_io;
    end else begin
        if (index[7]) begin
            if (data[6:0] == ADDRESS && header == 1'b1) begin
                sel <= 1;
                write <= sda_io; 
            end
        end
        if (index[8]) begin
            if (sel && !write && !header) begin
                data_o_reg <= data;
            end
            header <= 0;
        end 
        index[8:0] <= { index[7:0], index[8] };
        data[7:0] <= { data[6:0], sda_io };
    end
end

// write or ack on falling edge
always @(negedge scl_i) begin

    if (index[8]) begin
        if (sel && (header || !write)) 
            sda_io_reg <= 0;
        else
            sda_io_reg <= 1'bz;
        if (header) begin
            data_i_reg <= data_i;
        end
    end else if (write) begin
        if (data_i_reg[7]) 
            sda_io_reg <= 1'bz;
        else 
            sda_io_reg <= 1'b0;
        data_i_reg[7:0] <= { data_i_reg[6:0], 1'b1 };
    end else begin
        sda_io_reg <= 1'bz;
    end
    
end

endmodule
