//////////////////////////////////////////////////////////////////////////////////
// Company:			TAIR
// Engineer:		
// 
// Create Date:		10/30/2023 11:24:31 AM
// Design Name:
// Module Name:		SPIm
// Project Name:	S5443_V3_FPGA3
// Target Devices:	BOARD: BY5443v3. FPGA: xc7s25csga225-2
// Tool Versions:
// Description: 	This is module implements Spi Master protocol. For more info refer 
//					to the techincal reference manual. 
// 
// Dependencies: 
// 
// Revision:
// Revision 1.0 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
module SPIm 
(
    input Clk_i,
    input Rst_i,
    input Start_i,
    input EmptyFlag_i,
    input ClockPhase_i,
    input [31:0] SpiData_i,
    input SelSt_i,
    input [1:0] WidthSel_i,
    input  Lag_i,
    input  Lead_i,
    input EndianSel_i,
    input [5:0] Stop_i,
    input PulsePol_i,


    output reg Mosi0_o,
    output reg Sck_o,
    output  Ss_o,
    output reg  Val_o
);


//================================================================================
//	REG/WIRE
//================================================================================

    reg startFlag;
    reg valReg;
    reg lineBusy;
    reg [5:0] ssCnt;
    reg ss;
    
    reg ssR;
    reg SSR;
    reg [31:0] mosiReg0;
    reg [5:0] ssNum;
    reg [2:0] delayCnt;
    reg stopFlag;
    
    (* dont_touch = "true" *) wire [31:0] txLenght = ssNum+Lag_i+Lead_i;
    //================================================================================
    //  ASSIGNMENTS
    //================================================================================
    
    
    assign Ss_o = ss; 
    
    //================================================================================
    //	CODING
    //================================================================================
    
    always @(*) begin 
        if (Start_i) begin  
            Val_o = valReg;
        end
        else begin 
            Val_o = 1'b0;
        end
    end
    
    always @(*) begin 
        if (SelSt_i) begin 
            if (!Ss_o) begin 
                lineBusy = 1'b1;
            end
            else begin 
                lineBusy = 1'b0;
            end
        end
        else begin 
            if (Ss_o) begin 
                lineBusy = 1'b1;
            end
            else begin 
                lineBusy = 1'b0;
            end
        end
    end
    
	always @(negedge Clk_i) begin 
        if (Rst_i) begin 
            delayCnt <= 1'b0;
        end else begin 
			if (stopFlag) begin 
				delayCnt <= delayCnt + 1'b1;
			end else begin
				delayCnt <= 0;
			end
		end
    end
    
    always @(*) begin 
        if (Rst_i) begin 
            stopFlag = 1'b0;
        end
        else begin
			if (Stop_i != 0) begin
				if (SelSt_i) begin 
					if (ss && !ssR) begin 
						stopFlag = 1'b1;
					end
					else if (delayCnt == Stop_i-1) begin 
						stopFlag = 1'b0;
					end
				end
				else begin 
					if (!ss && ssR) begin 
						stopFlag = 1'b1;
					end
					else if (delayCnt == Stop_i-1) begin 
						stopFlag = 1'b0;
					end
				end
			end else begin
				stopFlag = 1'b0;
			end
        end
    end
    
	reg [2:0] clkCtrlReg;
	always @(*) begin
		if (Rst_i) 	begin
			clkCtrlReg = 0;
		end else begin
			clkCtrlReg = {SelSt_i,PulsePol_i,ClockPhase_i};
		end
	end
	
    always @(*) begin
		if (Rst_i) begin
			Sck_o = 0;
		end else begin
			if (Stop_i!=0) begin
				case (clkCtrlReg) 
					0:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (ss) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
					1:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (ss) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
					2:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (ss) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
					3:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (ss) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
					4:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (!ss) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
					5:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (!ss) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
					6:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (!ss) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = !Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
					7:	begin
							if (!Lag_i) begin
								if (!Lead_i) begin
									if (!ss) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end else begin
								if (!Lead_i) begin
									if (ssCnt > 0) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end else begin
									if (ssCnt >0 && ssCnt < txLenght-1) begin
										Sck_o = Clk_i;
									end else begin
										Sck_o = 0;
									end
								end
							end
						end
				endcase
			end else begin
				if (SelSt_i) begin
					if (!ss) begin
						if (PulsePol_i) begin 
							if (ClockPhase_i) begin
								Sck_o = Clk_i;
							end
							else begin 
								Sck_o = ~Clk_i;
							end
						end else begin
							if (ClockPhase_i) begin
								Sck_o = ~Clk_i;
							end
							else begin 
								Sck_o = Clk_i;
							end
						end
					end else begin
						Sck_o = 1'b0;
					end
				end else begin
					if (ss) begin
						if (PulsePol_i) begin 
							if (ClockPhase_i) begin
								Sck_o = Clk_i;
							end
							else begin 
								Sck_o = ~Clk_i;
							end
						end else begin
							if (ClockPhase_i) begin
								Sck_o = ~Clk_i;
							end
							else begin 
								Sck_o = Clk_i;
							end
						end
					end else begin
						Sck_o = 1'b0;
					end
				end
			end
		end
	end  
    
    always @(*) begin
        if (Rst_i) begin 
            Mosi0_o = 1'b0;
        end
        else begin
            if (SelSt_i) begin 
                if (!EndianSel_i) begin 
                    case (WidthSel_i)  
                        0 : begin
                            Mosi0_o = (!ss)? mosiReg0[7]:1'b0;
                        end
                        1 : begin
                            Mosi0_o = (!ss)? (mosiReg0[15]):1'b0;
                        end
                        2 : begin
                            Mosi0_o = (!ss)? (mosiReg0[23]):1'b0;
                        end
                        3 : begin
                            Mosi0_o = (!ss)? (mosiReg0[31]):1'b0;
                        end
                    endcase
                end
                else begin 
                    case (WidthSel_i)  
                        0 : begin
                            Mosi0_o = (!ss)? mosiReg0[0]:1'b0;
                        end
                        1 : begin
                            Mosi0_o = (!ss)? (mosiReg0[0]):1'b0;
                        end
                        2 : begin
                            Mosi0_o = (!ss)? (mosiReg0[0]):1'b0;
                        end
                        3 : begin
                            Mosi0_o = (!ss)? (mosiReg0[0]):1'b0;
                        end
                    endcase
                end
            end
            else begin 
                if (!EndianSel_i) begin 
                    case (WidthSel_i)  
                        0 : begin
                            Mosi0_o = (ss)? (mosiReg0[7]):1'b0;
                        end
                        1 : begin
                            Mosi0_o = (ss)? (mosiReg0[15]):1'b0;
                        end
                        2 : begin
                            Mosi0_o = (ss)? (mosiReg0[23]):1'b0;
                        end
                        3 : begin
                            Mosi0_o = (ss)? (mosiReg0[31]):1'b0;
                        end
                    endcase
                end
                else begin 
                    case (WidthSel_i)  
                        0 : begin
                            Mosi0_o = (ss)? (mosiReg0[0]):1'b0;
                        end
                        1 : begin
                            Mosi0_o = (ss)? (mosiReg0[0]):1'b0;
                        end
                        2 : begin
                            Mosi0_o = (ss)? (mosiReg0[0]):1'b0;
                        end
                        3 : begin
                            Mosi0_o = (ss)? (mosiReg0[0]):1'b0;
                        end
                    endcase
                end
            end
        end
    end

    always @(posedge Clk_i) begin
        ssR <= ss;
    end
    
    always @(*) begin 
        if (Rst_i) begin 
            startFlag = 1'b0;
        end
        else begin 
            if (Start_i && !stopFlag && !EmptyFlag_i ) begin 
                startFlag = 1'b1;
            end
            else begin 
                startFlag = 1'b0;
            end
        end
    end
    
    always @(posedge Clk_i) begin
        if (Rst_i) begin
			valReg <= 0;
		end else begin
			if (ssCnt == txLenght-3) begin
				if (!valReg) begin
					valReg <= 1;
				end else begin
					valReg <= 0;
				end
			end else begin
				valReg <= 0;
			end
		end
    end
    
    always @(*) begin 
        if (Rst_i) begin 
            ssNum = 1'b0;
        end
        else begin 
            case (WidthSel_i) 
                0 : begin 
                    ssNum = 8;
                end
                1 : begin 
                    ssNum = 16;
                end
                2 : begin 
                    ssNum = 24;
                end
                3 : begin 
                    ssNum = 32;
                end
            endcase
        end
    end
    
    
	always @(negedge Clk_i) begin 
        if (Rst_i) begin 
            ssCnt <= 0;
        end
        else begin
			if (SelSt_i) begin
				if (!ss) begin
					if (ssCnt != txLenght-1) begin 
						ssCnt <= ssCnt + 1;
					end else begin
						ssCnt <= 0;
					end
				end else begin
					ssCnt <= 0;
				end
			end else begin
				if (ss) begin
					if (ssCnt != txLenght-1) begin 
						ssCnt <= ssCnt + 1;
					end else begin
						ssCnt <= 0;
					end
				end else begin
					ssCnt <= 0;
				end
			end
		end
    end
    
    always @(negedge Clk_i) begin
		if (Rst_i) begin 
			ss <= 1'b1;
		end else begin
			if (Stop_i != 0) begin
				if (startFlag) begin
					if (SelSt_i) begin  
						if (ssCnt != txLenght-1) begin 
							ss <= 1'b0;
						end
						else begin 
							ss <= 1'b1;
						end
					end else begin
						if (ssCnt != txLenght-1) begin 
							ss <= 1'b1;
						end
						else begin 
							ss <= 1'b0;
						end
					end
				end else begin
					if (SelSt_i) begin  
						ss <= 1'b1;
					end else begin 
						ss <= 1'b0;
					end
				end
			end else begin
				if (startFlag) begin
					if (SelSt_i) begin
						ss <= 1'b0;
					end else begin
						ss <= 1'b1;
					end
				end else begin
					if (SelSt_i) begin
						ss <= 1'b1;
					end else begin
						ss <= 1'b0;
					end
				end
			end
        end
    end
    
    always @(negedge Clk_i) begin 
        if (Rst_i) begin 
            mosiReg0 <= SpiData_i[31:0];
        end
        else begin
			if (SelSt_i) begin
				if (!EndianSel_i) begin 
					if (Lag_i!=0) begin
						if (!ss&& (ssCnt > 0 && ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 << 1;
						end else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end else begin
						if (!ss&& (ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 << 1;
						end else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end
				end else begin 
					if (Lag_i!=0) begin
						if (!ss&& (ssCnt > 0 && ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 >> 1;
						end
						else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end else begin
						if (!ss&& (ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 >> 1;
						end else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end
				end
			end else begin
				if (!EndianSel_i) begin 
					if (Lag_i!=0) begin
						if (ss&& (ssCnt > 0 && ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 << 1;
						end else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end else begin
						if (ss&& (ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 << 1;
						end else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end
				end else begin 
					if (Lag_i!=0) begin
						if (ss&& (ssCnt > 0 && ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 >> 1;
						end
						else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end else begin
						if (ss&& (ssCnt < txLenght-1)) begin
							mosiReg0 <= mosiReg0 >> 1;
						end else begin 
							mosiReg0 <= SpiData_i[31:0];
						end
					end
				end
			end
        end
    end
  
endmodule