 /*                                                                      
 Copyright 2018 Nuclei System Technology, Inc.                
                                                                         
 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.                                          
 */                                                                      
                                                                         
                                                                         
                                                                         
/////////////////////////////////////////////////////////////////////
////                                                             ////
////  WISHBONE revB.2 compliant I2C Master controller Top-level  ////
////                                                             ////
////                                                             ////
////  Author: Richard Herveille                                  ////
////          richard@asics.ws                                   ////
////          www.asics.ws                                       ////
////                                                             ////
////  Downloaded from: http://www.opencores.org/projects/i2c/    ////
////                                                             ////
/////////////////////////////////////////////////////////////////////
////                                                             ////
//// 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.                                 ////
////                                                             ////
/////////////////////////////////////////////////////////////////////

//  CVS Log
//
//  $Id: i2c_master_top.v,v 1.11 2005/02/27 09:26:24 rherveille Exp $
//
//  $Date: 2005/02/27 09:26:24 $
//  $Revision: 1.11 $
//  $Author: rherveille $
//  $Locker:  $
//  $State: Exp $
//
// Change History:
//               $Log: i2c_master_top.v,v $
//               Revision 1.11  2005/02/27 09:26:24  rherveille
//               Fixed register overwrite issue.
//               Removed full_case pragma, replaced it by a default statement.
//
//               Revision 1.10  2003/09/01 10:34:38  rherveille
//               Fix a blocking vs. non-blocking error in the wb_dat output mux.
//
//               Revision 1.9  2003/01/09 16:44:45  rherveille
//               Fixed a bug in the Command Register declaration.
//
//               Revision 1.8  2002/12/26 16:05:12  rherveille
//               Small code simplifications
//
//               Revision 1.7  2002/12/26 15:02:32  rherveille
//               Core is now a Multimaster I2C controller
//
//               Revision 1.6  2002/11/30 22:24:40  rherveille
//               Cleaned up code
//
//               Revision 1.5  2001/11/10 10:52:55  rherveille
//               Changed PRER reset value from 0x0000 to 0xffff, conform specs.
//

// synopsys translate_off
//`include "timescale.v"
// synopsys translate_on

`include "i2c_master_defines.v"

module i2c_master_top(
    wb_clk_i, wb_rst_i, arst_i, wb_adr_i, wb_dat_i, wb_dat_o,
    wb_we_i, wb_stb_i, wb_cyc_i, wb_ack_o, wb_inta_o,
    scl_pad_i, scl_pad_o, scl_padoen_o, sda_pad_i, sda_pad_o, sda_padoen_o );

    // parameters
    parameter ARST_LVL = 1'b0; // asynchronous reset level

    //
    // inputs & outputs
    //

    // wishbone signals
    input        wb_clk_i;     // master clock input
    input        wb_rst_i;     // synchronous active high reset
    input        arst_i;       // asynchronous reset
    input  [2:0] wb_adr_i;     // lower address bits
    input  [7:0] wb_dat_i;     // databus input
    output [7:0] wb_dat_o;     // databus output
    input        wb_we_i;      // write enable input
    input        wb_stb_i;     // stobe/core select signal
    input        wb_cyc_i;     // valid bus cycle input
    output       wb_ack_o;     // bus cycle acknowledge output
    output       wb_inta_o;    // interrupt request signal output

    reg [7:0] wb_dat_o;
    reg wb_ack_o;
    reg wb_inta_o;

    // I2C signals
    // i2c clock line
    input  scl_pad_i;       // SCL-line input
    output scl_pad_o;       // SCL-line output (always 1'b0)
    output scl_padoen_o;    // SCL-line output enable (active low)

    // i2c data line
    input  sda_pad_i;       // SDA-line input
    output sda_pad_o;       // SDA-line output (always 1'b0)
    output sda_padoen_o;    // SDA-line output enable (active low)


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

    // generate internal reset
    wire rst_i = arst_i ^ ARST_LVL;

    // generate wishbone signals
    wire wb_wacc = wb_cyc_i & wb_stb_i & wb_we_i;

    // generate acknowledge output signal
    always @(posedge wb_clk_i or negedge rst_i)
    //always @(posedge wb_clk_i)//Bob: Here the ack is X by default, so add the rst here
      if (!rst_i)
        wb_ack_o <= #1 1'b0;
      else
        wb_ack_o <= #1 wb_cyc_i & wb_stb_i & ~wb_ack_o; // because timing is always honored

    // assign DAT_O
    always @(posedge wb_clk_i)
    begin
      case (wb_adr_i) // synopsis parallel_case
        3'b000: wb_dat_o <= #1 prer[ 7:0];
        3'b001: wb_dat_o <= #1 prer[15:8];
        3'b010: wb_dat_o <= #1 ctr;
        3'b011: wb_dat_o <= #1 rxr; // write is transmit register (txr)
        3'b100: wb_dat_o <= #1 sr;  // write is command register (cr)
        3'b101: wb_dat_o <= #1 txr;
        3'b110: wb_dat_o <= #1 cr;
        3'b111: wb_dat_o <= #1 0;   // reserved
      endcase
    end

    // generate registers
    always @(posedge wb_clk_i or negedge rst_i)
      if (!rst_i)
        begin
            prer <= #1 16'hffff;
            ctr  <= #1  8'h0;
            txr  <= #1  8'h0;
        end
      else if (wb_rst_i)
        begin
            prer <= #1 16'hffff;
            ctr  <= #1  8'h0;
            txr  <= #1  8'h0;
        end
      else
        if (wb_wacc)
          case (wb_adr_i) // synopsis parallel_case
             3'b000 : prer [ 7:0] <= #1 wb_dat_i;
             3'b001 : prer [15:8] <= #1 wb_dat_i;
             3'b010 : ctr         <= #1 wb_dat_i;
             3'b011 : txr         <= #1 wb_dat_i;
             //default: ; //Bob: here have a lint warning, so commented it out
          endcase

    // generate command register (special case)
    always @(posedge wb_clk_i or negedge rst_i)
      if (~rst_i)
        cr <= #1 8'h0;
      else if (wb_rst_i)
        cr <= #1 8'h0;
      else if (wb_wacc)
        begin
            if (core_en & (wb_adr_i == 3'b100) )
              cr <= #1 wb_dat_i;
        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 2'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      ( wb_clk_i     ),
        .rst      ( wb_rst_i     ),
        .nReset   ( rst_i        ),
        .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_pad_i    ),
        .scl_o    ( scl_pad_o    ),
        .scl_oen  ( scl_padoen_o ),
        .sda_i    ( sda_pad_i    ),
        .sda_o    ( sda_pad_o    ),
        .sda_oen  ( sda_padoen_o )
    );

    // status register block + interrupt request signal
    always @(posedge wb_clk_i or negedge rst_i)
      if (!rst_i)
        begin
            al       <= #1 1'b0;
            rxack    <= #1 1'b0;
            tip      <= #1 1'b0;
            irq_flag <= #1 1'b0;
        end
      else if (wb_rst_i)
        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 wb_clk_i or negedge rst_i)
      if (!rst_i)
        wb_inta_o <= #1 1'b0;
      else if (wb_rst_i)
        wb_inta_o <= #1 1'b0;
      else
        wb_inta_o <= #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
