//
// Simple I2C master devic, controlled by programmed I/O.
//
// Registers:
// 0 - write data/command
//     [15:8] - data bits (must be 1 for read)
//     [7]    - ACK bit (must be 1 for write)
//     [2:1]  - 00 = normal byte (S if needed, no following Sr/P)
//              01 = follow with Sr (S if needed)
//              10 = follow with P (S if needed)
//              11 = dummy clocks (no S, for synchronization)
//
// 1 - read data/status
//     [15:8] - data bits
//     [7]    - ack bit
//     [6]    - SDA
//     [5]    - SCL
//     [4]    - "started" (no S will be issued before next byte)
//     [2:1]  - bits [2:1] from last command
//     [0]    - busy
//
// 2 - baud rate divisor (f = clk/(4*(divisor+1)))
//
// 3 - bit [0] - send dummy clocks on SCL when idle
//
// This unit handles S(r) and P conditions by considering two classes of
// symbols: "normal", when SCL is asserted at the end of the

module i2c (
	    input	      rst_n,
	    input	      clk,

	    input	      valid,
	    input [1:0]       addr,
	    input [31:0]      wdata,
	    input [3:0]       wstrb,
	    output reg [31:0] rdata,
	    output	      irq,

	    inout	      i2c_scl,
	    inout	      i2c_sda
	    );

   reg [7:0]		  divisor;
   reg [7:0]		  baudctr;
   reg [3:0]		  bitctr;
   reg [1:0]		  phase;

   reg [8:0]		  wreg;	// Output shift register
   reg [8:0]		  rreg;	// Input shift register
   reg			  do_read; // Shift in a data bit next cycle

   reg			  busy;		// Data received, running
   reg			  end_s, end_p; // Trailing S(r) or P
   reg			  started;	// S sent, but not P

   reg [1:0]		  outsymb; // Output symbol [abnormal, data]

   reg			  scl_out = 1'b1;
   reg			  sda_out = 1'b1;

   assign i2c_scl = scl_out ? 1'bz : 1'b0;
   assign i2c_sda = sda_out ? 1'bz : 1'b0;

   always @(negedge rst_n or posedge clk)
     if (~rst_n)
       begin
	  bitctr  <= 4'd14;	// Idle line - wait for start condition
	  busy    <= 1'b0;
	  outsymb <= 2'b11;	// A1
	  baudctr <= 8'd0;
	  divisor <= 8'd209;	// 84 MHz -> 100 kHz
	  scl_out <= 1'b1;
	  sda_out <= 1'b1;
	  phase   <= 2'b00;
	  do_read <= 1'b0;
	  end_s   <= 1'bx;
	  end_p   <= 1'bx;
	  started <= 1'b0;
       end
     else
       begin
	  //
	  // I2C state machine
	  //

	  if (|baudctr)
	    begin
	       baudctr <= baudctr - 1'b1;
	    end
	  else
	    begin
	       // I2C clock event
	       baudctr <= divisor;

	       phase <= phase + 1'b1;
	       if ((phase == 2'b10) & ~i2c_scl)
		 phase <= 2'b10; // Clock stretch

	       // Phase 0: data is shifted out
	       // Phase 1: SCL goes high
	       // Phase 2: if SCL is low, hold
	       // Phase 3: SCL does low if symbol is normal,
	       //          sample SDA on read
	       if (phase[0])
		 scl_out <= outsymb[1] | ~phase[1];

	       sda_out <= outsymb[0];

	       if (phase == 2'b11)
		 begin
		    // Sample input and set up for the next cycle

		    if (do_read)
		      rreg <= { rreg[7:0], i2c_sda };

		    do_read <= 1'b0;

		    // Unit idle; send A0 or A1 depending on if we are
		    // started or not.
		    if (~busy)
		      begin
			 outsymb <= { 1'b1, ~started };
		      end
		    else
		      begin
			 bitctr  <= bitctr + 1'b1;
			 started <= 1'b1;

			 case (bitctr)
			   4'd0, 4'd1, 4'd2, 4'd3, 4'd4,
			   4'd5, 4'd6, 4'd7, 4'd8:
			     begin
				outsymb <= { 1'b0, wreg[8] }; // Nx
				wreg   <= { wreg[7:0], 1'b1 };
				do_read <= 1'b1;
				if (bitctr[3])
				  begin
				     bitctr  <= end_s ? 4'd14 :
						end_p ? 4'd12 : 4'b0;
				     busy    <= end_p & ~end_s;
				     // If we are to be followed by
				     // an S(r) condition, we are not
				     // really "started".
				     started <= ~(end_s | end_p);
				  end
			     end // case: 4'd0, 4'd1, 4'd2, 4'd3, 4'd4,...

			   // Stop condition
			   4'd12: begin
			      started <= 1'b0;
			      outsymb <= 2'b10; // A0
			   end

			   4'd13: begin
			      started <= 1'b0;
			      outsymb <= 2'b11; // A1
			      busy <= 1'b0;
			   end

			   // Start condition
			   4'd14: begin
			      started <= ~(end_s & end_p);
			      outsymb <= 2'b11; // A1
			   end

			   4'd15: begin
			      // N0, unless dummy in which case N1
			      outsymb <= { 1'b0, ~started };
			   end

			   default: begin
			      outsymb <= 2'bxx;
			   end
			 endcase // case (bitctr)
		      end // else: !if(~busy)
		 end // if (phase == 2'b11)
	    end // else: !if(|baudctr)

	  //
	  // CPU write interface
	  //
	  if (valid)
	    case (addr)
	      2'b00:
		 if (~busy)
		   begin
		      if (wstrb[1])
			wreg[8:1] <= wdata[15:8];
		      if (wstrb[0])
			begin
			   wreg[0] <= wdata[7];
			   end_p   <= wdata[2];
			   end_s   <= wdata[1];
			   busy    <= 1'b1;
			end
		   end // if (~busy)
	      2'b10: begin
		if (wstrb[0])
		  divisor <= wdata[7:0];
	      end
	      default: begin
		 // Do nothing
	      end
	    endcase // case (addr)
       end // else: !if(~rst_n)

   //
   // CPU read interface
   //
   always_comb
     case (addr)
       2'b00: rdata = { 16'b0, wreg, i2c_sda, i2c_scl, started, 1'b0,
			end_p, end_s, busy | do_read };
       2'b01: rdata = { 16'b0, rreg, i2c_sda, i2c_scl, started, 1'b0,
			end_p, end_s, busy | do_read };
       2'b10: rdata = { 24'b0, divisor };
       default: rdata = 32'bx;
     endcase // casez (addr)

   //
   // IRQ (level) when unit idle
   //
   assign irq = ~(busy | do_read);

endmodule // i2c
