/*
 Driver module for triple AD5644R on LLRF4

 Copyright (c) 2009 Dimtel, Inc., All Rights Reserved

 $Id: ad5644.v,v 1.2 2010/10/05 16:13:49 dim Exp $
 $Date: 2010/10/05 16:13:49 $
 $Author: dim $
 $Revision: 1.2 $
 $Log: ad5644.v,v $
 Revision 1.2  2010/10/05 16:13:49  dim
 Updates prior to iGp12 release

 Revision 1.1  2009/08/29 19:50:47  dim
 Initial import


*/

module ad5644(
  // Local bus interface
  input			clk,	// local clock
  input			strobe,	// bus strobe
  input			ce,	// clock enable / module select
  input			rd,	// read/write
  output reg		ack,	// ack from the module
  input [3:0]		addr,	// 4 bit address
  input [13:0]		din,	// from the bus
  output signed [31:0]	dout,	// data to the bus

  // DAC control lines
  (* IOB = "FALSE" *)
  output reg		sclk,	// serial clock
  (* IOB = "FALSE" *)
  output reg [2:0]	sdio,	// serial data
  (* IOB = "FALSE" *)
  output reg		cs_b	// chip select
);
  // Registers
  reg [23:0] sft_reg = 24'b0;
  parameter IDLE = 6'h3f; parameter START = 6'h00;
  parameter END = 6'h30;
  reg [5:0] state = IDLE;
  reg signed [13:0] data[11:0];
  reg [1:0] dev = 2'b0;
  reg mode = 1'b0;
  reg [13:0] val = 14'b0;

  initial begin
    ack  <= 1'b0;
    sclk <= 1'b1;
    sdio <= 3'b0;
    cs_b <= 1'b1;
  end
  
  always @(posedge clk) begin
    if (strobe & ce & ~ack & (state == IDLE))
      if (~rd) begin
        if (addr == 4'b1111) begin
          mode <= din[0];
          ack  <= 1'b1;
        end
        else if (mode == 1'b0)
          if (addr[3:2] == 2'b11) begin // Control write
            sft_reg <= {2'b0, 3'b111, 18'b0, din[0]};
            dev     <= addr[1:0];
            state   <= START;
            cs_b    <= 1'b0;
          end
          else begin
            sft_reg    <= {2'b0, 3'b011, 1'b0, addr[1:0], ~din[13], 
                           din[12:0], 2'b0};
            dev        <= addr[3:2];
            data[addr] <= din;
            state      <= START;
	    cs_b       <= 1'b0;
          end
        else // mode == 1
          ack <= 1'b1;
      end
      else
        ack <= 1'b1;
    if (ack) begin
      ack <= 1'b0;
    end
    
    if (mode == 1'b1 & state == IDLE) begin
      sft_reg   <= {2'b0, 3'b011, 3'b111, val, 2'b0};
      val   <= val + 14'b1;
      dev   <= 2'b11;
      state <= START;
      cs_b  <= 1'b0;
    end
    
    if (state != IDLE)
      if (state == END) begin
        state  <= IDLE;
        sclk   <= 1'b0;
        cs_b   <= 1'b1;
        if (mode == 1'b0) ack    <= 1'b1;
      end
      else begin
        state <= state + 6'b1;
        if (~state[0]) begin
          sdio[0] <= (dev == 2'b00 | dev == 2'b11) ? sft_reg[23] : 1'b0;
          sdio[1] <= (dev == 2'b01 | dev == 2'b11) ? sft_reg[23] : 1'b0;
          sdio[2] <= (dev == 2'b10 | dev == 2'b11) ? sft_reg[23] : 1'b0;
          sft_reg <= {sft_reg[22:0], 1'b0};
          sclk <= 1'b1;
        end
        else
          sclk <= 1'b0;
      end
  end

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

endmodule // ad5644
