/* I2C_driver_multiplexer.v - Implements a multiplexer for the SoC side of I2C_driver.v

   This file is part of the 9086 project.

   Copyright (c) 2024 Efthymios Kritikos

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

module I2C_driver_multiplexer (
	input wire clock,
	input wire reset_n,

	////// INPUT 1 ///////
	input wire [6:0] IN1_ADDRESS,
	output reg IN1_BUSY,
	input wire IN1_TRANSACT,
	input wire IN1_DIR,
	output reg [15:0] IN1_I2C_DATA_READ,
	input wire [15:0] IN1_I2C_DATA_WRITE,
	input IN1_TRANS_WIDTH,
	input IN1_IGN_ACK,
	output reg IN1_ERROR,

	////// INPUT 2 ///////
	input wire [6:0] IN2_ADDRESS,
	output reg IN2_BUSY,
	input wire IN2_TRANSACT,
	input wire IN2_DIR,
	output reg [15:0] IN2_I2C_DATA_READ,
	input wire [15:0] IN2_I2C_DATA_WRITE,
	input IN2_TRANS_WIDTH,
	input IN2_IGN_ACK,
	output reg IN2_ERROR,

	////// OUTPUT ///////
	output wire [6:0] OUT_ADDRESS,
	input wire OUT_BUSY,
	output reg OUT_TRANSACT,
	output wire OUT_DIR,
	input wire [15:0] OUT_I2C_DATA_READ,
	output wire [15:0] OUT_I2C_DATA_WRITE,
	output OUT_TRANS_WIDTH,
	output wire OUT_IGN_ACK,
	input wire OUT_ERROR
);

reg select;

assign OUT_TRANS_WIDTH =    select ? IN1_TRANS_WIDTH    : IN2_TRANS_WIDTH;
assign OUT_I2C_DATA_WRITE = select ? IN1_I2C_DATA_WRITE : IN2_I2C_DATA_WRITE;
assign OUT_ADDRESS =        select ? IN1_ADDRESS        : IN2_ADDRESS;
assign OUT_IGN_ACK =        select ? IN1_IGN_ACK        : IN2_IGN_ACK;
assign OUT_DIR=             select ? IN1_DIR            : IN2_DIR;

reg [1:0] STATE;
reg SERVICED;

always @(posedge clock)begin
	if(reset_n==1'b0)begin
		OUT_TRANSACT<=0;
		IN1_BUSY<=0;
		IN2_BUSY<=0;
		STATE<=2'd0;
	end else begin
		case(STATE)
			2'd0:begin
				if(IN1_TRANSACT&&OUT_BUSY==1'b0)begin
					select<=1'b1;
					OUT_TRANSACT<=1;
					STATE<=2'd1;
					SERVICED<=1'b0;
				end else if(IN2_TRANSACT&&OUT_BUSY==1'b0)begin
					select<=1'b0;
					OUT_TRANSACT<=1;
					IN1_BUSY<=1;
					STATE<=2'd1;
					SERVICED<=1'b1;
				end
				if(OUT_BUSY==1'b0)begin
					IN1_BUSY<=0;
					IN2_BUSY<=0;
				end
			end
			2'd1:begin
				if(OUT_BUSY==1'b1)begin
					STATE<=2'd0;
					OUT_TRANSACT<=0;
				end
				if(SERVICED==1'b0)begin
					IN1_BUSY<=1;
				end else begin
					IN2_BUSY<=1;
				end
			end
			default:begin
			STATE<=2'b0;
			end
		endcase
	end
end

always @(negedge OUT_BUSY)begin
	if(select)begin
		IN1_I2C_DATA_READ<=OUT_I2C_DATA_READ;
		IN1_ERROR<=OUT_ERROR;
	end else begin
		IN2_I2C_DATA_READ<=OUT_I2C_DATA_READ;
		IN2_ERROR<=OUT_ERROR;
	end
end

endmodule
