/*
 MCP3208 8 channel 12 bit ADC interface module

 $Id: mcp3208.v,v 1.1 2009/08/29 19:50:47 dim Exp $
 $Date: 2009/08/29 19:50:47 $
 $Author: dim $
 $Revision: 1.1 $
 $Log: mcp3208.v,v $
 Revision 1.1  2009/08/29 19:50:47  dim
 Initial import


*/

module mcp3208(
  // Local bus interface
  input			clk,	// local clock
  input			strobe,	// bus strobe
  output		ack,	// ack from the module
  input [2:0] 		addr,	// 3 bit address
  output [31:0]		dout,	// data to the bus

  // ADC interface lines
  output		cs,	// chip select
  input			sdo,	// data from the ADC
  output		sdi,	// data to the ADC
  output		sclk	// serial clock
);
  // Registers
  reg [11:0] sft = 12'b1000_0111_0000;
  parameter CTRL = 2'b00; parameter IDLE = 2'b01;
  parameter READ = 2'b11;
  reg [1:0] state = CTRL;
  reg [4:0] cnt = 5'h10;
  reg [11:0] data[7:0];
  reg [3:0] clkdiv = 4'b0;
  wire shift_ce;
  reg [2:0] chan = 3'b0;

  // Clock slowdown from 50 MHz to 2 MHz (500 ns period)
  assign shift_ce = (clkdiv == 4'b0);
  always @(posedge clk) clkdiv <= (clkdiv == 4'd11) ? 4'b0 : clkdiv + 4'b1;

  assign sdi  = sft[11];
  assign sclk = cnt[0];
  assign cs   = state == IDLE;
  
  // Bus interface section
  assign ack = strobe;

  always @(posedge clk) begin
    // MCP3208 interface state machine
    if (shift_ce) begin
      case (state)
        CTRL:
          if (cnt == 5'b0) begin
            cnt   <= 5'h1a;
            state <= READ;
          end
          else begin
            if (sclk) sft <= {sft[10:0], 1'b0};
            cnt <= cnt - 5'b1;
          end
        READ: if (cnt == 5'b0) begin
                data[chan] <= sft;
                chan  <= chan + 3'b1; // Next ADC channel
                state <= IDLE;
              end
              else begin
                if (sclk) sft <= {sft[10:0], sdo};
                cnt <= cnt - 5'b1;
              end
        IDLE: begin
          state <= CTRL;
          sft   <= {2'b11, chan[2], chan[1], chan[0], 7'b000_0000};
          cnt   <= 5'h0c; // 5 clocks to select channel, 1 to sample
        end
        endcase
    end
  end

  // Register readback
  assign dout = data[addr];

endmodule // mcp3208
