module oob_controller (

input               rst,              //reset
input               clk,

input               platform_ready,   //the underlying physical platform is
output  reg         platform_error,   //Underlyaing physal platform received an error, this should probably be a reset
output  reg         linkup,           //link is finished

output  reg         tx_comm_reset,     //send a init OOB signal
output  reg         tx_comm_wake,     //send a wake OOB signal
input               tx_oob_complete,  //Phy has said we are finished with this OOB transaction

input               comm_init_detect, //detected an init
input               comm_wake_detect, //detected a wake on the rx lines

input       [31:0]  rx_din,
input       [3:0]   rx_is_k,
input               rx_is_elec_idle,
input               rx_byte_is_aligned,
input               phy_error,

output  reg [31:0]  tx_dout,
output  reg         tx_is_k,
output  reg         tx_set_elec_idle,
output      [3:0]   lax_state


);

//platform signals

//Parameters

//States
parameter           IDLE                    = 4'h0;
parameter           SEND_RESET              = 4'h1;
parameter           WAIT_FOR_INIT           = 4'h2;
parameter           WAIT_FOR_NO_INIT        = 4'h3;
parameter           WAIT_FOR_CONFIGURE_END  = 4'h4;
parameter           SEND_WAKE               = 4'h5;
parameter           WAIT_FOR_WAKE           = 4'h6;
parameter           WAIT_FOR_NO_WAKE        = 4'h7;
parameter           WAIT_FOR_IDLE           = 4'h8;
parameter           WAIT_FOR_ALIGN          = 4'h9;
parameter           SEND_ALIGN              = 4'hA;
parameter           DETECT_SYNC             = 4'hB;
parameter           READY                   = 4'hC;

//Registers/Wires
reg         [3:0]   state;
reg         [31:0]  timer;
reg         [1:0]   no_align_count;
reg         [3:0]   retries;

//timer used to send 'INITs', WAKEs' and read them
wire                timeout;
wire                align_detected;
wire                sync_detected;

//Submodules
//Asynchronous Logic
assign              timeout         = (timer == 0);
//assign              align_detected  = ((rx_is_k > 0) && (rx_din == `PRIM_ALIGN) && !phy_error);
//assign              align_detected  = ((rx_is_k > 0) && (rx_din == `PRIM_ALIGN));
assign              align_detected  = ((rx_is_k > 0) && (rx_din == `PRIM_ALIGN) && rx_byte_is_aligned);
assign              sync_detected   = ((rx_is_k > 0) && (rx_din == `PRIM_SYNC));
assign              lax_state       = state;

//Synchronous Logic
initial begin
  tx_set_elec_idle      <=  1;
end

always @ (posedge clk) begin
  if (rst) begin
    state               <=  IDLE;
    linkup              <=  0;
    timer               <=  0;
    tx_comm_reset       <=  0;
    tx_comm_wake        <=  0;
    tx_dout             <=  0;
    tx_is_k             <=  0;
    tx_set_elec_idle    <=  1;
    no_align_count      <=  0;
    platform_error      <=  0;
    retries             <=  0;
  end
  else begin
    //to support strobes, continuously reset the following signals
    tx_comm_reset       <=  0;
    tx_comm_wake        <=  0;

    tx_is_k             <=  0;


    //timer (when reache 0 timeout has occured)
    if (timer > 0) begin
      timer                 <=  timer - 1;
    end

    //main state machine, if this reaches ready an initialization sequence has completed
    case (state)
      IDLE: begin
        platform_error      <=  0;
        linkup              <=  0;
        tx_set_elec_idle    <=  1;
        if (platform_ready) begin
          $display ("oob_controller: send RESET");
          //the platform is ready
          //  PLL has locked onto a clock
          //  DCM has generated the correct clocks
          timer             <=  32'h000000A2;
          state             <=  SEND_RESET;
          tx_comm_reset     <=  1;
        end
      end
      SEND_RESET: begin
//XXX: In the groundhog COMM RESET was continuously issued for a long period of time
        //send the INIT sequence, this will initiate a communication with the
        //SATA hard drive, or reset it so that it can be initiated to state

        //strobe the comm init so that the platform will send an INIT OOB signal
        if (timeout || tx_oob_complete) begin
          timer               <=  `INITIALIZE_TIMEOUT;
          state               <=  WAIT_FOR_INIT;
          $display ("oob_controller: wait for INIT");
        end
      end
      WAIT_FOR_INIT: begin
        //wait for a response from the SATA harddrive, if the timeout occurs
        //go back to the SEND_RESET state
        if (comm_init_detect) begin
          //HD said 'sup' go to a wake
          //timer             <=  0;
          timer             <=  32'h00001000;
          state             <=  WAIT_FOR_NO_INIT;
          $display ("oob_controller: wait for INIT to go low");
        end
        if (timeout) begin
          $display ("oob_controller: timed out while waiting for INIT");
          state             <=  IDLE;
        end
      end
      WAIT_FOR_NO_INIT: begin
        //wait for the init signal to go low from the device
        if (!comm_init_detect && (timeout || tx_oob_complete)) begin
          $display ("oob_controller: INIT deasserted");
          $display ("oob_controller: start configuration");
          state             <=  WAIT_FOR_CONFIGURE_END;
        end
      end
      WAIT_FOR_CONFIGURE_END: begin
          $display ("oob_controller: System is configured");
          state             <=  SEND_WAKE;
          timer             <=  32'h0000009B;
          tx_comm_wake      <=  1;
        //end
      end
      SEND_WAKE:  begin
//XXX: In the groundhog COMM WAKE was continuously send for a long period of time
        //Send the WAKE sequence to the hard drive to initiate a wakeup sequence
//XXX: Is this timeout correct?
        //880uS
        if (timeout || tx_oob_complete) begin
          //timer               <=  32'd`INITIALIZE_TIMEOUT;
          timer               <=  32'h000203AD;
          state               <=  WAIT_FOR_WAKE;
        end
      end
      WAIT_FOR_WAKE:  begin
        //Wait for the device to send a COMM Wake
        if (comm_wake_detect) begin
          //Found a comm wake, now wait for the device to stop sending WAKE
          timer             <=  0;
          state             <=  WAIT_FOR_NO_WAKE;
          $display ("oob_controller: WAKE detected");
        end
        if (timeout) begin
          //Timeout occured before reading WAKE
          state             <=  IDLE;
          $display ("oob_controller: timed out while waiting for WAKE to be asserted");
        end
      end
      WAIT_FOR_NO_WAKE: begin
        if (!comm_wake_detect) begin
          //The device stopped sending comm wake
//XXX: Is this timeout correct?
          //880uS
          $display ("oob_controller: detected WAKE deasserted");
          $display ("oob_controller: Send Dialtone, wait for ALIGN");
//Going to add more timeout
          //timer           <=  32'h0203AD;
          timer           <=  32'h0203AD;
          state           <=  WAIT_FOR_ALIGN;
          //state           <=  WAIT_FOR_IDLE;
          retries         <=  4;
        end
      end
      /*
      WAIT_FOR_IDLE: begin
        if (!rx_is_elec_idle) begin
          state           <=  WAIT_FOR_ALIGN;
          timer           <=  32'h0101D0;
        end
        else if (timeout) begin
          if (retries > 0) begin
            timer         <=  32'h0203AD;
            retries       <=  retries - 1;
          end
          if (retries == 0) begin
            state         <= IDLE;
          end
        end
      end
      */
      WAIT_FOR_ALIGN: begin
        //transmit the 'dialtone' continuously
        //since we need to start sending actual data (not OOB signals, get out
        //  of tx idle)
        tx_set_elec_idle    <=  0;
        //a sequence of 0's and 1's
        tx_dout             <=  `DIALTONE;
        tx_is_k             <=  0;
        //$display ("rx din: %h, k: %h", rx_din, rx_is_k);
        if (align_detected) begin
          //we got something from the device!
          timer             <=  0;
          //now send an align from my side
          state             <=  SEND_ALIGN;
          no_align_count    <=  0;
          $display ("oob_controller: ALIGN detected");
          $display ("oob_controller: Send out my ALIGNs");
        end
        if (timeout) begin
          //didn't read an align in time :( reset
          $display ("oob_controller: timed out while waiting for AIGN");
          state             <=  IDLE;
        end
      end
      SEND_ALIGN: begin
        tx_dout             <=  `PRIM_ALIGN;
        tx_is_k             <=  1;
        if (!align_detected) begin
          $display ("oob_controller: detected ALIGN deasserted");
//XXX: Groundhog detects the SYNC primitve before declaring linkup
          if (no_align_count == 3) begin
            $display ("oob_controller: ready");
            state           <=  READY;
          end
          else begin
            no_align_count  <=  no_align_count + 2'b01;
          end
        end
      end
      DETECT_SYNC: begin
        if (sync_detected) begin
          state             <=  READY;
        end
      end
      READY:  begin
        linkup              <=  1;
        /*
        if (phy_error) begin
          platform_error    <=  1;
        end
        */
        if (comm_init_detect) begin
          state             <=  IDLE;
        end
      end
      default: begin
        state               <=  IDLE;
      end
    endcase

  end
end

endmodule