`timescale 10ns/10ns

module top();

reg clk = 0;
reg rstn = 1;
reg adc_so = 1;
wire adc_si;
wire adc_sck;
wire adc_ss;

reg sck_old = 1;
reg ss_old = 1;

wire [1:0] channel;
wire [11:0] adc_val;
wire vld;

reg ack = 0;

adc_driver dut(
  .clk(clk),
  .rstn(rstn),
  .so(adc_so),
  .si(adc_si),
  .ss(adc_ss),
  .sck(adc_sck),

  .channel(channel),
  .val(adc_val),
  .vld(vld),
  .ack(ack)
  );

reg configured;

reg [15:0] out;

initial begin
  clk = 0;
  rstn = 1;
  adc_so = 1;
  sck_old = 1;

  configured = 0;
  out = 0;

  $dumpfile("adc_driver_tb.vcd");
  $dumpvars;
end

always
  #2 clk = !clk;

reg enabled;
integer bit_pos;
integer channel_num;
integer tosend;
integer configdata;
integer adc_pos;

integer adc_out[3:0];
integer expected[3:0];

integer i;
initial begin
  bit_pos = 0;
  channel_num = 0;
  tosend = 0;
  enabled = 0;
  configdata = 0;
  adc_pos = 0;
  adc_so = 1;

  for (i = 0; i < 4; i++) begin
    #1 adc_pos = i;
    #1 expected[i] = out;
  end
  adc_pos = 0;
  for (i = 0; i < 4; i++)
    adc_out[i] = 0;
end

always @* begin
  case (adc_pos[1:0])
    2'b00: out = 16'h01ff;
    2'b01: out = 16'h1fff;
    2'b10: out = 16'h2dea;
    2'b11: out = 16'h3caf;
  endcase

end

always @(posedge clk) begin
  if (~adc_ss & ss_old) begin
    enabled <= 1;
    //bit_pos <= 0;
  end
  if (enabled) begin
    if (!configured) begin
      // load into configdata if it hasn't
      // been configured yet
      if (~sck_old & adc_sck) begin
        configdata <= (configdata << 1) | adc_si;
        if (bit_pos >= 15) begin
          configured <= 1;
          bit_pos <= 0;
        end else
          bit_pos <= bit_pos + 1;
      end
    end else begin
      if (~sck_old & adc_sck) begin
        // TODO make sure the input is zero
      end
      if (sck_old & ~adc_sck) begin
        // otherwise start loading in adc data
        if (bit_pos >= 15) begin
          bit_pos <= 0;
        end else
          bit_pos <= bit_pos + 1;

        adc_so <= out[15-bit_pos];
      end
    end
  end

  if (vld & configured & ~ack) begin
    adc_pos <= adc_pos + 1;
    adc_out[channel] = adc_val;
    ack <= 1;
    if (channel == 3) begin
      // TODO check configdata
      i = {
        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
      };

      if (configdata != i) 
        $display("config %d != %d", configdata, i);
      for (i = 0; i < 4; i++) begin
        if (adc_out[i] != (12'hfff & expected[i]) )
          $display("adc %x %x != %x", i, adc_out[i], expected[i]);
      end
      $finish;
    end
  end else
    ack <= 0;

  ss_old <= adc_ss;
  sck_old <= adc_sck;
end

initial begin
  #50000
  $finish;
end


endmodule