//
// Chuck Benz ASIC & FPGA Design
//
//  CGSS: Chuck's generic SPI slave core

module cbaf_cgss (
    input               spi_clk,
    input               rstl,
    input               spi_select,
    input               mosi,
    output reg          miso,

    input [7:0]         status1,
    output reg [7:0]    control1
                  );

   parameter            ADDR_WIDTH = 8,
                        DATA_WIDTH = 8 ;

   reg [127:0]          shift_in ; // over-sized, synth will trim.
   reg [7:0]            command ;
   reg [ADDR_WIDTH-1:0] addr ;
   reg [ADDR_WIDTH-1:0] addr_D ;
   reg [DATA_WIDTH-1:0] wr_data ;
   reg [DATA_WIDTH-1:0] rd_data_D ;
   reg [7:0]            spi_counter ;
   reg [7:0]            shift_out ;
   reg                  spi_selectQ ;
   reg                  mosiQ ;

   always @ (*) begin
      addr_D = addr ;
      if (spi_counter == (6 + ADDR_WIDTH))
        addr_D[ADDR_WIDTH-1:1] = {shift_in[ADDR_WIDTH-3:0], mosiQ} ;
      if (spi_counter == (7 + ADDR_WIDTH))
        addr_D[0] = mosiQ ;

      rd_data_D = 0 ;
      case ({addr[ADDR_WIDTH-1:1], addr_D[0]})
        0: rd_data_D = control1 ;
        1: rd_data_D = status1 ;
      endcase

   end

   always @ (negedge spi_clk or negedge rstl)
     if (~rstl)
       miso <= 0 ;
     else
       miso <= (command[0] == 0) && (spi_selectQ == 0) &&
               // send msbit of rd data
               ((rd_data_D[DATA_WIDTH-1] && (spi_counter ==(7+ADDR_WIDTH))) ||
                // and the rest of the rd data
                shift_out[DATA_WIDTH-2]) ;

   always @ (posedge spi_clk or negedge rstl)
     if (~rstl) begin
        /*AUTORESET*/
        // Beginning of autoreset for uninitialized flops
        addr <= {(1+(ADDR_WIDTH-1)){1'b0}};
        command <= 8'h0;
        control1 <= 8'h0;
        mosiQ <= 1'h0;
        shift_in <= 128'h0;
        shift_out <= 8'h0;
        spi_counter <= 8'h0;
        spi_selectQ <= 1'h0;
        // End of automatics
     end
     else begin
        // ensure use of IOB flops on SS and MOSI
        spi_selectQ <= spi_select ;
        mosiQ       <= mosi ;
        shift_in    <= {shift_in, mosiQ} ;

        addr <= addr_D ;
        if (spi_counter == 8)
          command <= shift_in[7:0] ;

        if (spi_selectQ)
          spi_counter <= 0 ;
        else if (~spi_selectQ && (spi_counter == 0)) begin
           spi_counter <= 1 ;
        end
        else begin // spi_selectQ is 0, spi_counter is already counting
           if (spi_counter == (7 + ADDR_WIDTH + DATA_WIDTH))
             spi_counter <= 8 + ADDR_WIDTH ;
           else
             spi_counter <= spi_counter + 1 ;
        end

        if (command[0] && (spi_selectQ == 0) &&
            (spi_counter == (7 + ADDR_WIDTH + DATA_WIDTH)))
          case (addr)
            0: control1 <= {shift_in, mosiQ} ;
          endcase

        if (spi_counter == (7 + ADDR_WIDTH))
          shift_out <= rd_data_D ;
        else
          shift_out <= shift_out << 1 ;
     end

endmodule
