/*
 DS1822 interface

 $Id: ds1822.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: ds1822.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 ds1822(
  // Local bus interface
  input			clk,	// local clock
  input			strobe,	// bus strobe
  output		ack,	// ack from the module
  input [1:0] 		addr,	// 2 bit address
  output signed [31:0]	dout,	// data to the bus
  output signed [15:0]  temperature, // internal output for feedback

  // DS1822 line: direction, output and input
  output		dq_dir,	// DS1822 pin direction
  output		dq_out, // DS1822 drive value, always 0
  input			dq_in
);
  reg [7:0] sft_out = 8'b0;
  reg [6:0] sft_in = 7'b0;
  
  // Main state machine
  localparam RESET         = 4'b0000;
  localparam PRESENCE      = 4'b0001;
  localparam PRESENCE_END  = 4'b1010;
  localparam STOP          = 4'b0010;
  localparam READ_ROM_CMD  = 4'b0011;
  localparam READ_ROM      = 4'b0100;
  localparam CONVERT_T_CMD = 4'b0101;
  localparam CONVERT_T     = 4'b0110;
  localparam SKIP_ROM_CMD  = 4'b0111; 
  localparam READ_SPAD_CMD = 4'b1000;
  localparam READ_SPAD     = 4'b1001;

  // Substates
  localparam SUB_INITIAL = 2'b00;
  localparam SUB_READ    = 2'b01;
  localparam SUB_CONVERT = 2'b10;
  
  // Data transfer states
  localparam DTF_IDLE       = 3'b000;
  localparam DTF_SLOT_START = 3'b001;
  localparam DTF_SEC1       = 3'b010;
  localparam DTF_SEC2       = 3'b011;
  localparam DTF_RECOVERY   = 3'b100;

  // Commands for DS1822
  localparam CMD_READROM = 8'h33;
  localparam CMD_SKIPROM = 8'hcc;
  localparam CMD_CONVERT = 8'h44;
  localparam CMD_READSPD = 8'hbe;
  
  localparam RESET_CYCLES         = 9'd480; // 480 uS
  localparam PRESENCE_CYCLES      = 9'd60;  // 60 uS
  localparam PRES_RECOVERY_CYCLES = 9'd420; // 420 uS
  localparam TSLOT_INIT_CYCLES    = 9'd1;   // 1 uS
  localparam READ_SAMPLE_CYCLES   = 9'd13;  // 13 uS
  localparam READ_WAIT_CYCLES     = 9'd46;  // 46 uS (60-14)
  localparam RECOVERY_CYCLES      = 9'd1;   // 1 uS
  
  // Counter hold state
  localparam CNT_HOLD = 15'h7fff;

  reg [3:0] state    = RESET;
  reg [1:0] substate = SUB_INITIAL;
  reg [2:0] byte_state = DTF_IDLE;
  reg [8:0] cnt = RESET_CYCLES;
  reg [5:0] us_cnt = 6'b0;
  reg [3:0] data_adr= 4'b0;
  wire action = (cnt == 9'b0);
  wire us_tick = (us_cnt == 6'b0);
  reg [7:0] data[9:0];
  reg status = 1'b0;
  reg [6:0] bits = 7'b0;
  reg drive = 1'b1, transfer = 1'b0, read = 1'b0;

  // DS1822 interface
  assign dq_dir = drive;
  assign dq_out = 1'b0;

  // Bus interface section
  assign ack = strobe;

  // Main state machine
  always @(posedge clk)
    // Tick counter
    us_cnt <= (us_tick) ? 6'd47 : us_cnt - 6'b1;
  
  always @(posedge clk) begin
    if (us_tick) begin
      case (state)
        RESET:
          if (action) begin
            state    <= PRESENCE;
            cnt      <= PRESENCE_CYCLES;
            drive    <= 1'b0;
          end
        PRESENCE:
          if (action) begin
            // Sample the data
            status <= ~dq_in;
            if (dq_in)
              state <= STOP;
            else
              state <= PRESENCE_END;
              cnt   <= PRES_RECOVERY_CYCLES;
          end
        PRESENCE_END:
          if (action)
            if (substate == SUB_INITIAL) begin
              state    <= READ_ROM_CMD;
              sft_out  <= CMD_READROM;
              bits     <= 7'b0001_000; // 1 byte
              read     <= 1'b0;
              transfer <= 1'b1;
            end
            else begin
              state    <= SKIP_ROM_CMD;
              sft_out  <= CMD_SKIPROM;
              bits     <= 7'b0001_000; // 1 byte
              read     <= 1'b0;
              transfer <= 1'b1;
            end
        STOP: begin // Did not see the device, try again
          state    <= RESET;
          drive    <= 1'b1;
          cnt      <= RESET_CYCLES;
          substate <= SUB_INITIAL;
        end
        READ_ROM_CMD:
          if (~transfer) begin
            state    <= READ_ROM;
            data_adr <= 4'b0000;
            bits     <= 7'b1000_000; // 8 bytes
            read     <= 1'b1;
            transfer <= 1'b1;
          end
        READ_ROM:
          if (~transfer) begin
            state     <= RESET;
            substate  <= SUB_CONVERT;
            cnt       <= RESET_CYCLES;
            drive     <= 1'b1;
            read      <= 1'b0;
          end
        CONVERT_T_CMD:
          if (~transfer) begin
            state    <= CONVERT_T;
            data_adr <= 4'b1111;
            bits     <= 7'b0000_001; // 1 bit
            read     <= 1'b1;
            transfer <= 1'b1;
          end
        CONVERT_T:
          if (~transfer)
            if (sft_in[6]) begin// Read back 1
              state    <= RESET;
              substate <= SUB_READ;
              cnt      <= RESET_CYCLES;
              drive    <= 1'b1;
              read     <= 1'b0;
            end
            else begin // Read back 0, continue waiting
              data_adr <= 4'b1111;
              bits     <= 7'b0000_001; // 1 bit
              read     <= 1'b1;
              transfer <= 1'b1;
            end
        SKIP_ROM_CMD:
          if (~transfer)
            if (substate == SUB_CONVERT) begin
              state    <= CONVERT_T_CMD;
              sft_out  <= CMD_CONVERT;
              bits     <= 7'b0001_000; // 1 byte
              read     <= 1'b0;
              transfer <= 1'b1;
            end
            else begin
              state    <= READ_SPAD_CMD;
              sft_out  <= CMD_READSPD;
              bits     <= 7'b0001_000; // 1 byte
              read     <= 1'b0;
              transfer <= 1'b1;
            end
        READ_SPAD_CMD:
          if (~transfer) begin
            state    <= READ_SPAD;
            data_adr <= 4'b1000; // Address 8
            bits     <= 7'b0010_000; // 2 bytes
            read     <= 1'b1;
            transfer <= 1'b1;
          end
        READ_SPAD:
          if (~transfer) begin
            state    <= RESET;
            substate <= SUB_CONVERT;
            cnt      <= RESET_CYCLES;
            drive    <= 1'b1;
            read     <= 1'b0;
          end
      endcase

      // Data byte read/write state machine
      case (byte_state)
        DTF_IDLE: if (transfer) begin
          cnt        <= TSLOT_INIT_CYCLES;
          byte_state <= DTF_SLOT_START;
          drive      <= 1'b1;
        end
        DTF_SLOT_START:
          if (action) begin
            drive      <= ~read & ~sft_out[0];
            sft_out    <= {1'b0, sft_out[7:1]};
            cnt        <= READ_SAMPLE_CYCLES;
            byte_state <= DTF_SEC1;
          end
        DTF_SEC1:
          if (action) begin
            cnt        <= READ_WAIT_CYCLES;
            byte_state <= DTF_SEC2;
            sft_in     <= {dq_in, sft_in[6:1]};
            bits       <= bits - 7'b1;
            if ((bits[2:0] == 3'b001) & read & (data_adr != 4'b1111)) begin
              data[data_adr] <= {dq_in, sft_in[6:0]};
              data_adr       <= data_adr + 4'b1;
            end
          end
        DTF_SEC2:
          if (action) begin
            cnt        <= RECOVERY_CYCLES;
            byte_state <= DTF_RECOVERY;
            drive      <= 1'b0;
          end
        DTF_RECOVERY:
          if (action)
            if (bits == 7'b0) begin
              byte_state <= DTF_IDLE;
              transfer   <= 1'b0;
            end
            else begin
              cnt        <= TSLOT_INIT_CYCLES;
              byte_state <= DTF_SLOT_START;
              drive      <= 1'b1;
            end
      endcase

      // Coarse counter
      if (~action) cnt <= cnt - 9'b1;
    end
  end

  // Register readback
  assign temperature = {data[9], data[8]};
  assign dout = (addr == 2'b00) ? {data[3], data[2], data[1], data[0]} :
                (addr == 2'b01) ? {data[7], data[6], data[5], data[4]} :
                (addr == 2'b10) ? temperature : status;

endmodule // ds1822
