///////////////////////////////////////////
//
// Written: James Stine
// Modified: 9/28/2021
//
// Purpose: FSM for floating point divider/square root unit (Goldschmidt)
// 
// A component of the Wally configurable RISC-V project.
// 
// Copyright (C) 2021 Harvey Mudd College & Oklahoma State University
//
// MIT LICENSE
// Permission is hereby granted, free of charge, to any person obtaining a copy of this 
// software and associated documentation files (the "Software"), to deal in the Software 
// without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons 
// to whom the Software is furnished to do so, subject to the following conditions:
//
//   The above copyright notice and this permission notice shall be included in all copies or 
//   substantial portions of the Software.
//
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//   INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
//   PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
//   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
//   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 
//   OR OTHER DEALINGS IN THE SOFTWARE.
////////////////////////////////////////////////////////////////////////////////////////////////

module fsm_fpdiv (
   input logic 	      clk,
   input logic 	      reset,
   input logic 	      start,
   input logic 	      op_type,
   output logic       done, 
   output logic       load_rega, 
   output logic       load_regb, 
   output logic       load_regc, 
   output logic       load_regd,
   output logic       load_regr,
   output logic       load_regs,
   output logic [2:0] sel_muxa, 
   output logic [2:0] sel_muxb, 
   output logic       sel_muxr, 
   output logic       divBusy	   
   );

   typedef enum       logic [4:0] {S0, S1, S2, S3, S4, S5, S6, S7, S8, S9,
				   S10, S11, S12, S13, S14, S15, S16, S17, S18, S19,
				   S20, S21, S22, S23, S24, S25, S26, S27, S28, S29,
				   S30} statetype;
   
   statetype current_state, next_state;
   
   always @(posedge clk)
     begin
	if (reset == 1'b1)
	  current_state = S0;
	else
	  current_state = next_state;
     end

   always_comb
     begin
 	case(current_state)
	  S0:  // iteration 0
	    begin
	       if (start==1'b0)
		 begin
		    done = 1'b0;
		    divBusy = 1'b0;	
		    load_rega = 1'b0;
		    load_regb = 1'b0;
		    load_regc = 1'b0;
		    load_regd = 1'b0;
		    load_regr = 1'b0;
		    load_regs = 1'b0;
		    sel_muxa = 3'b000;
		    sel_muxb = 3'b000;
		    sel_muxr = 1'b0;
		    next_state = S0;
		 end 
	       else if (start==1'b1 & op_type==1'b0) 
		 begin
		    done = 1'b0;
		    divBusy = 1'b1;	
		    load_rega = 1'b0;
		    load_regb = 1'b1;
		    load_regc = 1'b0;
		    load_regd = 1'b0;		    
		    load_regr = 1'b0;
		    load_regs = 1'b0;		    		    
		    sel_muxa = 3'b001;
		    sel_muxb = 3'b001;		    
		    sel_muxr = 1'b0;
		    next_state = S1;
		 end // if (start==1'b1 & op_type==1'b0)
	       else if (start==1'b1 & op_type==1'b1) 
		 begin
		    done = 1'b0;
		    divBusy = 1'b1;
		    load_rega = 1'b0;
		    load_regb = 1'b1;
		    load_regc = 1'b0;
		    load_regd = 1'b0;		    
		    load_regr = 1'b0;
		    load_regs = 1'b0;		    		    
		    sel_muxa = 3'b010;
		    sel_muxb = 3'b000;		    
		    sel_muxr = 1'b0;
		    next_state = S13;
		 end 	   
	       else
		 begin
		    done = 1'b0;
		    divBusy = 1'b0;
		    load_rega = 1'b0;
		    load_regb = 1'b0;
		    load_regc = 1'b0;
		    load_regd = 1'b0;		    
		    load_regr = 1'b0;
		    load_regs = 1'b0;		    		    
		    sel_muxa = 3'b000;
		    sel_muxb = 3'b000;		    
		    sel_muxr = 1'b0;
		    next_state = S0;
		 end
	    end // case: S0
	  S1:
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b010;
	       sel_muxb = 3'b000;		    
	       sel_muxr = 1'b0;	
	       next_state = S2;
	    end	  
	  S2: // iteration 1
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b1;
	       load_regc = 1'b0;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b011;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S3;
	    end
	  S3:
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b010;
	       sel_muxr = 1'b0;
	       next_state = S4;
	    end
	  S4: // iteration 2
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b1;
	       load_regc = 1'b0;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b011;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S5;
	    end
	  S5:
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b010;
	       sel_muxr = 1'b0;  // add
	       next_state = S6;
	    end
	  S6: // iteration 3
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b1;
	       load_regc = 1'b0;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;
	       sel_muxa = 3'b011;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S8;
	    end
	  S7:
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b010;
	       sel_muxr = 1'b0;
	       next_state = S8;
	    end // case: S7
	  S8: // q,qm,qp
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b1;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b0;
	       next_state = S9;
	    end 
	  S9:  // rem
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b1;
	       load_regs = 1'b0;  
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b1;
	       next_state = S10;
	    end 	  
	  S10:  // done
	    begin
	       done = 1'b1;
	       divBusy = 1'b0;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b0;
	       next_state = S11;
	    end // case: S10
	  S11:  // done
	    begin
	       done = 1'b0;
	       divBusy = 1'b0;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b0;
	       next_state = S0;
	    end 	  
	  S13:  // start of sqrt path
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b1;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;
	       sel_muxa = 3'b010;
	       sel_muxb = 3'b001;
	       sel_muxr = 1'b0;
	       next_state = S14;
	    end
	  S14:  
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b001;
	       sel_muxb = 3'b100;
	       sel_muxr = 1'b0;
	       next_state = S15;
	    end 
	  S15:  // iteration 1
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b1;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b011;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S16;
	    end
	  S16:  
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b1;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S17;
	    end
	  S17:  
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b100;
	       sel_muxb = 3'b010;
	       sel_muxr = 1'b0;
	       next_state = S18;
	    end
	  S18:  // iteration 2
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b1;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b011;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S19;
	    end
	  S19:  
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b1;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S20;
	    end
	  S20:  
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b100;
	       sel_muxb = 3'b010;
	       sel_muxr = 1'b0;
	       next_state = S21;
	    end
	  S21:  // iteration 3
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b1;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b011;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S22;
	    end
	  S22:  
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b1;
	       load_regr = 1'b0;
	       load_regs = 1'b0;
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b011;
	       sel_muxr = 1'b0;
	       next_state = S23;
	    end
	  S23:  
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b1;
	       load_regb = 1'b0;
	       load_regc = 1'b1;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b100;
	       sel_muxb = 3'b010;
	       sel_muxr = 1'b0;
	       next_state = S24;
	    end 
	  S24: // q,qm,qp
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;
	       load_regr = 1'b0;
	       load_regs = 1'b1;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b0;
	       next_state = S25;
	    end 	  
	  S25:  // rem
	    begin
	       done = 1'b0;
	       divBusy = 1'b1;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b1;
	       load_regs = 1'b0;  
	       sel_muxa = 3'b011;
	       sel_muxb = 3'b110;
	       sel_muxr = 1'b1;
	       next_state = S26;
	    end 
	  S26:  // done
	    begin
	       done = 1'b1;
	       divBusy = 1'b0;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b0;
	       next_state = S27;
	    end // case: S26
	  S27:  // done
	    begin
	       done = 1'b0;
	       divBusy = 1'b0;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b0;
	       next_state = S0;
	    end 	  
	  default: 
	    begin
	       done = 1'b0;
	       divBusy = 1'b0;
	       load_rega = 1'b0;
	       load_regb = 1'b0;
	       load_regc = 1'b0;
	       load_regd = 1'b0;	       
	       load_regr = 1'b0;
	       load_regs = 1'b0;		    	       
	       sel_muxa = 3'b000;
	       sel_muxb = 3'b000;
	       sel_muxr = 1'b0;
	       next_state = S0;
	    end
	endcase // case(current_state)	
     end // always @ (current_state or X)   

endmodule // fsm
