// driver for the MAX1141 ADC used in the board
// it has 4 analog inputs, and is configured to automatically
// scan them sequentially while being driven by SCK
// it divides the input clock by 16 to produce the SPI clock

// it writes one configuration word to the ADC and then constantly
// polls it for new data
module adc_driver(
  input clk,
  input rst,

  input so,
  output si,
  output ss,
  output sck,

  output [1:0] channel,
  output [11:0] val,
  output vld,
  input ack
);

reg [1:0] channel_ff = 0;
reg [11:0] adc_val_ff = 0;
reg new = 0;

assign channel = channel_ff;
assign val = adc_val_ff;
assign vld = new & ~ack;

reg sck_strobe = 0;
wire strobe = sck_strobe;
wire strobe2 = ~sck_strobe;

always @(posedge clk) begin
  if (~rst)
    sck_strobe <= sck_strobe + 1;
  else
    sck_strobe <= 0;
end

localparam INIT = 0, CONFIG = 1, ADC = 2;
reg [1:0] state = INIT;
reg [1:0] state_next;

reg [4:0] bit_pos = 0;
reg [4:0] bit_pos_next;

reg [15:0] so_ff = 0;
reg [15:0] so_ff_next;

reg write_out;
reg sck_next;
reg ss_next;
reg si_next;

reg si_ff = 1;
reg ss_ff = 1;
reg sck_ff = 1;
assign si = si_ff;
assign ss = ss_ff;
assign sck = sck_ff;

wire [15:0] config_word;
wire config_bit;
assign config_word = {
      1'b0, // ADC Mode Control
      4'b0100, // Standard_Ext
      4'b0011, // Up to AIN3
      2'b01, // reset FIFO
      2'b00, // normal PM mode
      1'b1, // include channel number in output
      1'b0, // SWCNV enable, not used in external clock mode
      1'b0 // reserved
    };
assign config_bit = config_word[15-bit_pos[4:1]];

always @* begin
  state_next = state;
  bit_pos_next = bit_pos;
  write_out = 0;
  so_ff_next = so_ff;

  sck_next = 1;
  ss_next = 1;
  si_next = 0;

  if (~rst) begin
    // latch sck and ss state until the strobe happens
    si_next = si;
    ss_next = ss;
    sck_next = sck;

    if (strobe2) begin
      if (state == CONFIG || state == ADC) begin
          // deassert slave select so it can be triggered on the next frame
          if (bit_pos == 31 & ~ss_next & ~sck) begin
            ss_next = 1;
            //bit_pos_next = 0; // don't need this because it's going to
                                // overflow
          end else begin
            ss_next = 0;
          end
      end
    end
    if (strobe) begin
      case (state)
        INIT: begin
          ss_next = 0;
          state_next = CONFIG;
          bit_pos_next = 0;
        end
        CONFIG: begin
          sck_next = bit_pos[0];
          bit_pos_next = bit_pos + 1;

          // update on the falling edge
          if (sck) begin
            si_next = config_bit;
          end
          // switch state on the rising edge after the overflow
          // and reassert ss to start the next frame
          if (~sck & (bit_pos == 31)) begin
            //ss_next = 0;
            state_next = ADC;
          end
        end
        ADC: begin
          //ss_next = 0;
          si_next = 0;
          sck_next = bit_pos[0];
          bit_pos_next = bit_pos + 1;

          // update bit pos state on the rising edge
          // shift in data as well
          if (~sck) begin
            so_ff_next = {so_ff[14:0], so};

            // after reading the last bit 
            // deassert ss so it can be reasserted on the next rising edge
            if (bit_pos == 31) begin
              //ss_next = 1;
              write_out = 1;
            end
          end
        end
      endcase
    end
  end else begin
    state_next = INIT;
    bit_pos_next = 0;
    sck_next = 1;
    ss_next = 1;
  end
end

always @(posedge clk) begin
  state <= state_next;
  bit_pos <= bit_pos_next;

  sck_ff <= sck_next;
  ss_ff <= ss_next;
  si_ff <= si_next;
  so_ff <= so_ff_next;

  // write the data out when write_out is asserted
  if (write_out) begin
    {channel_ff, adc_val_ff} <= so_ff_next[13:0];
    new <= 1;
  end
  // deassert vld when the data is acknowledged
  if (ack)
    new <= 0;
end

endmodule
