`timescale 1ns/1ns

module cpu_interface #( 
	parameter 	STATUS_REGISTER		= 8'H1C, 	// Read Only
	parameter	COMMAND_REGISTER	= 8'H1C, 	// Write Only
	parameter   FAULT_REGISTER		= 8'H1D, 	// Read / Clear on Write
	parameter 	WARNING_REGISTER	= 8'H1E,	// Read / Clear on Write
	parameter	SWITCH_REGISTER		= 8'H1F,	// Read Only
	parameter	TEST_REGISTER		= 8'H20,	// Read / Write	
	parameter	FAULTS				= 8,
	parameter	WARNINGS			= 6
)(	
	input					clock_i,
	input					reset_i,
	input					as_n_i,
	input					rd_n_i,
	input					wr_n_i,	 
	input					remote_burst_i, 
	input					remote_squeeze_i, 
	input					remote_empty_i,
	input [7:0]				incoming_data_i,
	input [5:0]				switches_i,
	input [FAULTS-1:0]		faults_i,
	input[2:0]				warnings_i,	
	input[2:0]				emptys_i,
	input[2:0]				bursts_i,
	input[2:0]				squeezes_i,
	output reg [7:0]		outgoing_data_o, 
	output					motors_disabled_o, 
	output					resample_o, 
	output					home_configuration_o,	
	output 					water_alarm_enable_o,
	output 					pressure_sensor_enable_o,
	output 					pressure_sensor_select_o,
	output 					drive_bus_o,
	output [FAULTS-1:0]		faults_o,
	output					water_faults_o
);

reg						previous_as_n;
reg						previous_rd_n;
reg						previous_wr_n; 	 

// Software visible registers
reg  [7:0]				address_register;
reg  [7:0]				diagnostic_register;
reg	 [5:0]				command_register;
reg  [FAULTS - 1:0]		faults;
reg  [WARNINGS - 1:0]	warnings;
	  
wire					clear_faults;
wire					clear_warnings;
wire					any_faults;
wire					any_warnings;						

//  Register the AS_N to find it's rising and falling edges
always @(negedge clock_i or posedge reset_i)
	if (reset_i) previous_as_n <= 1'b1;
	else previous_as_n <= as_n_i;	

//  Register the RD_N to find it's rising and falling edges		
always @(negedge clock_i or posedge reset_i)
	if (reset_i) previous_rd_n <= 1'b1;
	else previous_rd_n <= rd_n_i;

//  Register the WR_N to find it's rising and falling edges
always @(negedge clock_i or posedge reset_i)
	if (reset_i) previous_wr_n <= 1'b1;
	else previous_wr_n <= wr_n_i;	
		
// Drive data to the MSP430, if a read occurs to parameter ADDRESS
// Otherwise, keep the CPLD AD[7:0] pins high impedance
// assign drive_bus_o = (address_register >= STATUS_REGISTER) & (address_register <= TEST_REGISTER) & ~rd_n_i;
assign drive_bus_o = (address_register >= STATUS_REGISTER) & (address_register <= 8'h23) & ~rd_n_i;

// Implements Address Register	  
// TODO:  Describe how AS_N and rising edge of RD_N and WR_N are used
always @(negedge clock_i or posedge reset_i)
	if (reset_i) 
		address_register <= 8'b0;				  // Clear
	else if (~as_n_i &  previous_as_n) 
		address_register <= incoming_data_i;		  // Load
	else if ( rd_n_i & ~previous_rd_n & ~previous_as_n) 
		address_register <= address_register + 8'd1; // Read autoincrment
	else if ( wr_n_i & ~previous_wr_n & ~previous_as_n) 
		address_register <= address_register + 8'd1; // Write autoincrement
	else address_register <= address_register; 	  // Hold 
		
// Implements the write only command register
always @(negedge clock_i or posedge reset_i)
	if (reset_i) 
		command_register <= 6'b100000;			 	// Clear
	else if ((address_register == COMMAND_REGISTER) & ~wr_n_i & previous_wr_n ) 
		command_register <= { incoming_data_i[7:5], incoming_data_i[2:0]};				// Load if write
	else 
		command_register <= command_register;		// Hold	
		
// Implements write portion of diagnostic register
always @(negedge clock_i or posedge reset_i)
	if (reset_i) 
		diagnostic_register <= 3'b000;	 	// Clear
	else if ((address_register == TEST_REGISTER) & ~wr_n_i & previous_wr_n ) 
		diagnostic_register <= incoming_data_i[7:0];		// Load if write
	else 
		diagnostic_register <= diagnostic_register;	// Hold	   

assign clear_faults 	= ((address_register == FAULT_REGISTER)   & ~wr_n_i & previous_wr_n);
assign clear_warnings	= ((address_register == WARNING_REGISTER) & ~wr_n_i & previous_wr_n);		

genvar i;

generate
	for(i = 0 ; i < FAULTS ; i = i + 1)
		begin : bits_of_fault_register
			always @(negedge clock_i or posedge reset_i)
				if (reset_i) 
					faults[i] <= 1'b0;	  									
				else if (clear_faults & incoming_data_i[i]) 
					faults[i] <= 1'b0;									    
				else if (faults_i[i] & ~command_register[5])
					faults[i] <= 1'b1;	
		end
endgenerate			
		
generate
	for(i = 0 ; i < WARNINGS ; i = i + 1)
		begin : bits_of_warning_register
			always @(negedge clock_i or posedge reset_i)
				if (reset_i) 
					warnings[i] <= 1'b0;	  									
				else if (clear_warnings & incoming_data_i[i]) 
					warnings[i] <= 1'b0;									    
				else if (switches_i[i] & ~command_register[5])
					warnings[i] <= 1'b1;	
		end
endgenerate	  

// Reduction "or" of all faults and warnings into single bits
assign any_faults 	= (command_register[4]) ? (|faults[7:2]) : ( |{faults[7:6], faults[1:0]} );
assign any_warnings	= |warnings;

// Implements read mux for returning data to the MSP430
always @* 
	case (address_register)
		STATUS_REGISTER		: outgoing_data_o	<= { any_faults, any_warnings, 3'b000, remote_burst_i, remote_squeeze_i, remote_empty_i};
		FAULT_REGISTER		: outgoing_data_o  	<= { 2'b00, faults};  
		WARNING_REGISTER	: outgoing_data_o  	<= { 2'b00, warnings };    
		TEST_REGISTER		: outgoing_data_o  	<= diagnostic_register;	
		8'h21				: outgoing_data_o	<= { 1'b0, warnings_i[0], switches_i[1:0], 1'b0, emptys_i };
		8'h22				: outgoing_data_o	<= { 1'b0, warnings_i[2], switches_i[5:4], 1'b0, bursts_i };  
		8'h23				: outgoing_data_o	<= { 1'b0, warnings_i[1], switches_i[3:2], 1'b0, squeezes_i };
		default				: outgoing_data_o  	<= 8'HXX;
	endcase

assign { motors_disabled_o, resample_o, home_configuration_o } = command_register[5:3];
assign { water_alarm_enable_o, pressure_sensor_enable_o, pressure_sensor_select_o } = command_register[2:0];	
assign water_faults_o = faults[7] | faults[6];	  
assign faults_o = faults;

endmodule