// module spi_master
// Author: M. Walz

module spi_master(
  //module inputs
  // --- Definition of control inputs ---
  input wire        RESETn,
  input wire        CLK,
  input wire [7:0]  CLK_DIVIDER,
  input wire [7:0]  SLAVE_SELECT,
  input wire [1:0]  DATA_LENGTH,
  input wire [1:0]  MODE,
  input wire        MISO,
  input wire [31:0] TX,
  input wire RUN,
  //module outputs
  output reg [31:0] RX,
  output            SCLK,
  output reg        MOSI,
  output reg [7:0]  SSn,
  output reg        BUSY,
  // module test outputs
  output wire       SYNC_TEST,
  output wire [2:0] STATE_TEST,
  output wire       ENA_TEST  
  );


  
  
// --- Definition of internal varibles ---
  reg [31:0]TX_SR, RX_SR;
  reg [7:0] CLK_DIVIDER_REG;
  reg [5:0] CYCLE_CTR;
  reg [2:0] STATE;
  reg ENA;                // Enables operation of SCLK generator
  reg T1, Q1;             // Used for SLCK generation
  wire CPOL, CPHA;        // SPI Mode
  wire SYNC;              // SYNC Signal
  
// --- Implementation ---

// assignments of test signals
  assign STATE_TEST = STATE;
  assign ENA_TEST = ENA;
  assign SYNC_TEST = SYNC;

// assignments MODE to control wires  
  assign CPOL = MODE [1];
  assign CPHA = MODE [0];
  
// Clockdivider for generation of SYNC signal

always @ (posedge CLK or negedge RESETn) begin
end

//Definition der States
parameter STATE_WAIT = 3'h0;
parameter STATE_INIT = 3'h1;
parameter STATE_SHIFT = 3'h2;
parameter STATE_LATCH = 3'h3;
parameter STATE_CLEAR = 3'h4;

reg [2:0] NEXT_STATE;

// SPI-interface control logic

//Bei jedem Takt wird der State auf den nächsten State gesetzt, wenn ein Reset anliegt wird der State 'Wait' vorgegeben.
always @(posedge CLK) begin
    if(reset) begin
        STATE = STATE_WAIT;
    end
    else begin
        STATE = NEXT_STATE;
    end
end

always @ (posedge CLK or negedge RESETn) begin
  if (~RESETn) begin
    NEXT_STATE = STATE_WAIT;
    TX_SR[31:0] = 32'h0;
    RX_SR[31:0] <= 32'h0;
    CYCLE_CTR[5:0] <= 5'h0;
    SSn[7:0] <= 8'hFF;
    ENA <= 1'b0;
    MOSI <= 1'b0;
    RX <= 32'b0;
    BUSY <= 1'b0;
  end 
  else case (STATE)
    STATE_WAIT:
      if (RUN == 1'b1) begin
          NEXT_STATE <= STATE_INIT;
      end
      else begin
          NEXT_STATE <= STATE_WAIT;
      end

    STATE_INIT: 
      if (SYNC == 1'b1) begin
          NEXT_STATE <= STATE_SHIFT;
          if(CPHA) begin
            SSn [7:0] <= (~SLAVE_SELECT);
          end
          ENA <= 1'b1;
          case (DATA_LENGTH[1:0]) begin
            2'h0:
              CYCLE_CTR[5:0] <= 6'h8;
              TX_SR[31:24] <= TX[7:0];
          end
          endcase
      end
      else begin
          NEXT_STATE <= STATE_INIT;
      end

    STATE_SHIFT:
      if (SYNC == 1'b1) begin
          NEXT_STATE <= STATE_LATCH;
      end
      else begin
          NEXT_STATE <= STATE_SHIFT;
      end

    STATE_LATCH:
      if (SYNC == 1'b1 && CYCLE_CTR != 6'h0) begin
          NEXT_STATE <= STATE_SHIFT;
      end
      else if (SYNC == 1'b1 && CYCLE_CTR == 6'h0) begin
          NEXT_STATE <= STATE_CLEAR;
      end
      else begin
          NEXT_STATE <= STATE_LATCH;
      end

    STATE_CLEAR:
      if (SYNC == 1'b1) begin
          NEXT_STATE <= STATE_WAIT;
      end
      else begin
          NEXT_STATE <= STATE_CLEAR;
      end

    default: NEXT_STATE <= STATE_WAIT;

	// When transfering data from RX to RX_SR, ignore previously received bytes
				case (DATA_LENGTH[1:0])
					2'h0 : RX[31:0] <= {24'h0, RX_SR[ 7:0]};	// 1 Byte received
					2'h1 : RX[31:0] <= {16'h0, RX_SR[15:0]}; // 2 Bytes received // 2 Bytes received	
					2'h2 : RX[31:0] <= {8'h0, RX_SR[23:0]}; // 2 Bytes received// 3 Bytes received
					2'h3 : RX_SR[ 31:0];                   // 4 Bytes received
				endcase

  endcase
end

always @(STATE) begin
    case (STATE)
        STATE_WAIT: 

        STATE_INIT: 

        STATE_SHIFT: 
            CYCLE_CTR <= CYCLE_CTR - 1;

        STATE_LATCH: 

        STATE_CLEAR: 
        
        default: 
    endcase
end

//  SPI SCLK generation

//SCLK_X Variablen
reg SCLK_0;
reg SCLK_1;
reg SCLK_2;
reg SCLK_3;

//D-FlipFlops
always @(posedge CLK or negedge RESETn) begin
    if(~RESETn) begin
        T1 <= 1'b0;
        Q1 <= 1'b0;
    end
    else if (SYNC) begin
        T1 <= ENA && ~T1;
        Q1 <= T1;
    end
end

//EXOR
always @(*) begin
    SCLK_1 = CPHA && T1;
    SCLK_0 = Q1 && ~CPHA;
    SCLK_2 = SCLK_0 || SCLK_1;
    SCLK_3 = SCLK_2 ^ CPOL;
    SCLK = SCLK_3;
end

endmodule
