module sata_transport_layer (
  input               rst,            //reset
  input               clk,
  input               phy_ready,


//Transport Layer Control/Status
  output              transport_layer_ready,
  input               sync_escape,

  output  reg         d2h_reg_stb,
  output  reg         dma_activate_stb,
  output  reg         d2h_data_stb,
  output  reg         dma_setup_stb,
  output  reg         pio_setup_stb,
  output  reg         set_device_bits_stb,

  output  reg         remote_abort,
  output              xmit_error,
  output              read_crc_error,

  input               send_command_stb,
  input               send_control_stb,
  input               send_data_stb,

//PIO
  output reg          pio_response,
  output reg          pio_direction,
  output reg  [15:0]  pio_transfer_count,
  output reg  [7:0]   pio_e_status,


//Host to Device Registers
  input       [7:0]   h2d_command,
  input       [15:0]  h2d_features,
  input       [7:0]   h2d_control,
  input       [3:0]   h2d_port_mult,
  input       [7:0]   h2d_device,
  input       [47:0]  h2d_lba,
  input       [15:0]  h2d_sector_count,

//Device to Host Registers
  output  reg [7:0]   d2h_fis,
  output  reg         d2h_interrupt,
  output  reg         d2h_notification,
  output  reg [3:0]   d2h_port_mult,
  output  reg [7:0]   d2h_device,
  output  reg [47:0]  d2h_lba,
  output  reg [15:0]  d2h_sector_count,
  output  reg [7:0]   d2h_status,
  output  reg [7:0]   d2h_error,

//DMA Specific Control

//Data Control
  input               cl_if_ready,
  output reg          cl_if_activate,
  input       [23:0]  cl_if_size,

  output              cl_if_strobe,
  input       [31:0]  cl_if_data,

  input       [1:0]   cl_of_ready,
  output  reg [1:0]   cl_of_activate,
  output              cl_of_strobe,
  output      [31:0]  cl_of_data,
  input       [23:0]  cl_of_size,


//Link Layer Interface
  input               link_layer_ready,
  output              ll_sync_escape,

  output              ll_write_start,
  input               ll_write_strobe,
  input               ll_write_finished,
  output      [31:0]  ll_write_data,
  output      [23:0]  ll_write_size,
  output              ll_write_hold,
  output              ll_write_abort,
  input               ll_xmit_error,

  input               ll_read_start,
  output              ll_read_ready,
  input       [31:0]  ll_read_data,
  input               ll_read_strobe,
  input               ll_read_finished,
  input               ll_read_crc_ok,
  input               ll_remote_abort,

  output      [3:0]   lax_state
);


//Parameters
parameter           IDLE                  = 4'h0;
parameter           READ_FIS              = 4'h1;
parameter           WAIT_FOR_END          = 4'h2;


parameter           CHECK_FIS_TYPE        = 4'h1;
parameter           WRITE_H2D_REG         = 4'h2;
parameter           RETRY                 = 4'h3;
parameter           READ_D2H_REG          = 4'h4;
parameter           READ_PIO_SETUP        = 4'h5;
parameter           READ_SET_DEVICE_BITS  = 4'h6;
parameter           DMA_ACTIVATE          = 4'h7;
parameter           SEND_DATA             = 4'h8;
parameter           READ_DATA             = 4'h9;


//Registers/Wires
reg         [3:0]   fis_id_state;
reg         [7:0]   current_fis;
reg         [3:0]   state;
reg                 detect_fis;
wire                processing_fis;

//data direction
wire                data_direction;

//Detect FIS from the device
wire                detect_d2h_reg;
wire                detect_dma_activate;
wire                detect_dma_setup;
wire                detect_d2h_data;
wire                detect_pio_setup;
wire                detect_set_device_bits;

//control data signals
wire        [31:0]  register_fis          [5:0];
reg         [7:0]   register_fis_ptr;
reg                 cmd_bit;

reg                 reg_write_start;
wire        [31:0]  reg_write_data;
wire        [23:0]  reg_write_size;
reg                 reg_write_ready;
wire                reg_write_hold;
wire                reg_write_abort;
wire                reg_write_strobe;

//read register
wire                reg_read;
wire                reg_write;
reg         [7:0]   reg_read_count;
wire                reg_read_stb;



//data state machine signals
reg                 data_write_start;
wire                data_write_strobe;
wire                data_read_strobe;
wire        [23:0]  data_write_size;
wire        [31:0]  data_write_data;
wire                data_write_hold;
wire                data_write_abort;

reg                 data_read_ready;
reg                 send_data_fis_id;

reg                 ll_write_finished_en;



//Asnchronous Logic
assign  lax_state               = state;
assign  transport_layer_ready   = (state == IDLE) && link_layer_ready;
assign  ll_sync_escape          = sync_escape;
assign  xmit_error              = ll_xmit_error;

//Attach Control/Data Signals to link layer depending on the conditions

//Write Control
assign  ll_write_start          = (reg_write)   ? reg_write_start                : data_write_start;
assign  ll_write_data           = (reg_write)   ? register_fis[register_fis_ptr] : data_write_data;
assign  ll_write_size           = (reg_write)   ? reg_write_size                 : data_write_size;
assign  ll_write_hold           = (reg_write)   ? 1'b0                           : data_write_hold;
assign  ll_write_abort          = (reg_write)   ? 1'b0                           : data_write_abort;
assign  cl_if_strobe            = (reg_write)   ? 1'b0                           : (!send_data_fis_id && data_write_strobe);

//Read Control
assign  ll_read_ready           = (reg_read)    ? 1'b1                           : data_read_ready;
assign  cl_of_strobe            = (reg_read)    ? 1'b0                           : ((state == READ_DATA) && data_read_strobe);
assign  cl_of_data              = ll_read_data;

//Data Register Write Control Signals
assign  data_write_data         = (send_data_fis_id)     ? {24'h000, `FIS_DATA}  : cl_if_data;
                                                      //the first DWORD is the FIS ID
assign  data_write_size         = cl_if_size + 24'h1;
                                                      //Add 1 to the size so that there is room for the FIS ID
assign  data_write_strobe       = ll_write_strobe;
assign  data_read_strobe        = ll_read_strobe;
assign  data_write_hold         = 1'b0;
                                                   //There should never be a hold on the data becuase the CL will set it up
assign  data_write_abort        = 1'b0;
assign  read_crc_error          = !ll_read_crc_ok;


//H2D Register Write control signals
assign  reg_write_strobe        = ll_write_strobe;
assign  reg_write_size          = `FIS_H2D_REG_SIZE;
assign  reg_write_hold          = 1'b0;
assign  reg_write_abort         = 1'b0;
assign  reg_write               = (state == WRITE_H2D_REG)        || (send_command_stb || send_control_stb);

//D2H Register Read control signals
assign  reg_read                = (state == READ_D2H_REG)         ||  detect_d2h_reg          ||
                                  (state == READ_PIO_SETUP)       ||  detect_pio_setup        ||
                                  (state == READ_SET_DEVICE_BITS) ||  detect_set_device_bits;

assign  reg_read_stb            = ll_read_strobe;

//Data Read control signals

//Detect Signals
assign  processing_fis          = (state == READ_FIS);

assign  detect_d2h_reg          = detect_fis ? (ll_read_data[7:0] == `FIS_D2H_REG)       : (current_fis == `FIS_D2H_REG      );
assign  detect_dma_activate     = detect_fis ? (ll_read_data[7:0] == `FIS_DMA_ACT)       : (current_fis == `FIS_DMA_ACT      );
assign  detect_d2h_data         = detect_fis ? (ll_read_data[7:0] == `FIS_DATA)          : (current_fis == `FIS_DATA         );
assign  detect_dma_setup        = detect_fis ? (ll_read_data[7:0] == `FIS_DMA_SETUP)     : (current_fis == `FIS_DMA_SETUP    );
assign  detect_pio_setup        = detect_fis ? (ll_read_data[7:0] == `FIS_PIO_SETUP)     : (current_fis == `FIS_PIO_SETUP    );
assign  detect_set_device_bits  = detect_fis ? (ll_read_data[7:0] == `FIS_SET_DEV_BITS)  : (current_fis == `FIS_SET_DEV_BITS );

assign  register_fis[0]         = {h2d_features[7:0], h2d_command, cmd_bit, 3'b000, h2d_port_mult, `FIS_H2D_REG};
assign  register_fis[1]         = {h2d_device, h2d_lba[23:0]};
assign  register_fis[2]         = {h2d_features[15:8], h2d_lba[47:24]};
assign  register_fis[3]         = {h2d_control, 8'h00, h2d_sector_count};
assign  register_fis[4]         = {32'h00000000};

//Synchronous Logic
//FIS ID State machine
always @ (posedge clk) begin
  if (rst) begin
    fis_id_state                <=  IDLE;
    detect_fis                  <=  0;
    current_fis                 <=  0;
    d2h_fis                     <=  0;
  end
  else begin
    //in order to set all the detect_* high when the actual fis is detected send this strobe
    if(ll_read_finished) begin
      current_fis               <=  0;
      fis_id_state              <=  IDLE;
    end
    else begin
      case (fis_id_state)
        IDLE: begin
          current_fis           <=  0;
          detect_fis            <=  0;
          if (ll_read_start) begin
            detect_fis          <=  1;
            fis_id_state        <=  READ_FIS;
          end
        end
        READ_FIS: begin
          if (ll_read_strobe) begin
            detect_fis          <=  0;
            current_fis         <=  ll_read_data[7:0];
            d2h_fis             <=  ll_read_data[7:0];
            fis_id_state        <=  WAIT_FOR_END;
          end
        end
        WAIT_FOR_END: begin
          if (ll_read_finished) begin
            current_fis         <=  0;
            fis_id_state        <=  IDLE;
          end
        end
        default: begin
          fis_id_state          <=  IDLE;
        end
      endcase
    end
  end
end


//main DATA state machine
always @ (posedge clk) begin
  if (rst) begin
    state                       <=  IDLE;
    cl_of_activate              <=  0;
    cl_if_activate              <=  0;
    data_read_ready             <=  0;

    //Data Send

    send_data_fis_id            <=  0;

    //H2D Register
    register_fis_ptr            <=  0;
    reg_write_start             <=  0;

    //D2H Register
    reg_read_count              <=  0;

    //Device Status
    d2h_interrupt               <=  0;
    d2h_notification            <=  0;
    d2h_port_mult               <=  0;
    d2h_lba                     <=  0;
    d2h_sector_count            <=  0;
    d2h_status                  <=  0;
    d2h_error                   <=  0;

    d2h_reg_stb                 <=  0;
    d2h_data_stb                <=  0;
    pio_setup_stb               <=  0;
    set_device_bits_stb         <=  0;
    dma_activate_stb            <=  0;
    dma_setup_stb               <=  0;

    remote_abort                <=  0;

    cmd_bit                     <=  0;

    //PIO
    pio_transfer_count          <=  0;
    pio_e_status                <=  0;
    pio_direction               <=  0;
    pio_response                <=  0;

    data_write_start            <=  0;

    ll_write_finished_en        <=  0;
  end
  else begin
    //Strobed signals
    if (phy_ready) begin
      //only deassert a link layer strobe when Phy is ready and not sending aligns
      data_write_start          <=  0;
      reg_write_start           <=  0;
    end

    d2h_reg_stb                 <=  0;
    d2h_data_stb                <=  0;
    pio_setup_stb               <=  0;
    set_device_bits_stb         <=  0;
    dma_activate_stb            <=  0;
    dma_setup_stb               <=  0;

    remote_abort                <=  0;



    d2h_device                  <=  0;
    //Always attempt to get a free buffer
    if ((cl_of_activate == 0) && (cl_of_ready > 0)) begin
      if (cl_of_ready[0]) begin
        cl_of_activate[0]       <= 1;
      end
      else begin
        cl_of_activate[1]       <= 1;
      end
      data_read_ready           <= 1;
    end
    else if (cl_of_activate == 0) begin
//XXX: NO BUFFER AVAILABLE!!!! Can't accept new data from the Hard Drive
      data_read_ready           <= 0;
    end

    //Always attempt to get the incomming buffer
    if (cl_if_ready && !cl_if_activate) begin
      cl_if_activate            <=  1;
    end

    if (ll_write_finished) begin
        ll_write_finished_en    <= 1;
    end

    case (state)
      IDLE: begin
        register_fis_ptr        <=  0;
        reg_read_count          <=  0;
        cmd_bit                 <=  0;
        ll_write_finished_en    <=  0;
        //Detect a FIS
        if(ll_read_start) begin
          //detect the start of a frame
          state                 <=  CHECK_FIS_TYPE;
//Clear Errors when a new transaction starts
        end
        //Send Register
        if (send_command_stb || send_control_stb) begin
//Clear Errors when a new transaction starts

          if (send_command_stb) begin
            cmd_bit               <=  1;
          end
          else if (send_control_stb) begin
            cmd_bit               <=  0;
          end

          reg_write_start       <=  1;
          state                 <=  WRITE_H2D_REG;
        end
        //Send Data
        else if (send_data_stb) begin
//Clear Errors when a new transaction starts
          data_write_start      <=  1;
          send_data_fis_id      <=  1;
          state                 <=  SEND_DATA;
        end
      end
      CHECK_FIS_TYPE: begin
        if (detect_dma_setup) begin
//XXX: Future work!
          reg_read_count        <=  reg_read_count + 8'h1;
          state                 <=  IDLE;
        end
        else if (detect_dma_activate) begin
          //hard drive is ready to receive data
          state                 <=  DMA_ACTIVATE;
          reg_read_count        <=  reg_read_count + 8'h1;
          //state                 <=  IDLE;
        end
        else if (detect_d2h_data) begin
          //incomming data, because the out FIFO is directly connected to the output during a read
          state                 <=  READ_DATA;
        end
        else if (detect_d2h_reg) begin
          //store the error, status interrupt from this read
          d2h_status            <=  ll_read_data[23:16];
          d2h_error             <=  ll_read_data[31:24];
          d2h_interrupt         <=  ll_read_data[14];
          d2h_port_mult         <=  ll_read_data[11:8];

          state                 <=  READ_D2H_REG;
          reg_read_count        <=  reg_read_count + 8'h1;
        end
        else if (detect_pio_setup) begin
          //store the error, status, direction interrupt from this read
          pio_response          <=  1;

          d2h_status            <=  ll_read_data[23:16];
          d2h_error             <=  ll_read_data[31:24];
          d2h_interrupt         <=  ll_read_data[14];
          pio_direction         <=  ll_read_data[13];
          d2h_port_mult         <=  ll_read_data[11:8];

          state                 <=  READ_PIO_SETUP;
          reg_read_count        <=  reg_read_count + 8'h1;
        end
        else if (detect_set_device_bits) begin
          //store the error, a subset of the status bit and the interrupt
          state                 <=  IDLE;
          d2h_status[6:4]       <=  ll_read_data[22:20];
          d2h_status[2:0]       <=  ll_read_data[18:16];
          d2h_error             <=  ll_read_data[31:24];
          d2h_notification      <=  ll_read_data[15];
          d2h_interrupt         <=  ll_read_data[14];
          d2h_port_mult         <=  ll_read_data[11:8];

          state                 <=  READ_SET_DEVICE_BITS;
        end
        else if (ll_read_finished) begin
          //unrecognized FIS
          state                 <=  IDLE;
        end
      end
      WRITE_H2D_REG: begin
        if  (register_fis_ptr < `FIS_H2D_REG_SIZE) begin
          if (reg_write_strobe) begin
            register_fis_ptr    <=  register_fis_ptr + 8'h1;
          end
        end
        if (ll_write_finished_en) begin
          if (ll_xmit_error) begin
            state               <=  RETRY;
          end
          else begin
            state               <=  IDLE;
          end
        end
      end
      RETRY: begin
        if (link_layer_ready) begin
          ll_write_finished_en  <=  0;    
          reg_write_start       <=  1;
          register_fis_ptr      <=  0;
          state                 <=  WRITE_H2D_REG;
        end
      end
      READ_D2H_REG: begin
        case (reg_read_count)
          1: begin
            d2h_device          <=  ll_read_data[31:24];
            d2h_lba[23:0]       <=  ll_read_data[23:0];
          end
          2: begin
            d2h_lba[47:24]      <=  ll_read_data[23:0];
          end
          3: begin
            d2h_sector_count    <=  ll_read_data[15:0];
          end
          4: begin
          end
          default: begin
          end
        endcase
        if (reg_read_stb) begin
          reg_read_count        <=  reg_read_count + 8'h1;
        end
        if (ll_read_finished) begin
          d2h_reg_stb           <=  1;
          state                 <=  IDLE;
        end
      end
      READ_PIO_SETUP: begin
        case (reg_read_count)
          1: begin
            d2h_device          <=  ll_read_data[31:24];
            d2h_lba[23:0]       <=  ll_read_data[23:0];
          end
          2: begin
            d2h_lba[47:24]      <=  ll_read_data[23:0];
          end
          3: begin
            d2h_sector_count    <=  ll_read_data[15:0];
            pio_e_status        <=  ll_read_data[31:24];
          end
          4: begin
            pio_transfer_count  <=  ll_read_data[15:0];
          end
          default: begin
          end
        endcase
        if (reg_read_stb) begin
          reg_read_count        <=  reg_read_count + 8'h1;
        end
        if (ll_read_finished) begin
          pio_setup_stb         <=  1;
          state                 <=  IDLE;
        end
      end
      READ_SET_DEVICE_BITS: begin
        state                 <=  IDLE;
        set_device_bits_stb   <=  1;
      end
      DMA_ACTIVATE: begin
        state                 <=  IDLE;
        dma_activate_stb      <=  1;
      end
      SEND_DATA: begin
        if (ll_write_strobe && send_data_fis_id) begin
          send_data_fis_id      <=  0;
        end
        if (ll_write_finished_en) begin
          cl_if_activate        <=  0;
          state                 <=  IDLE;
          if (pio_response) begin
            //write the end status to the status pin
            d2h_status          <=  pio_e_status;
          end
        end
      end
      READ_DATA: begin
        //NOTE: the data_read_ready will automatically 'flow control' the data from the link layer
        //so we don't have to check it here
        //NOTE: We don't have to keep track of the count because the lower level will give a max of 2048 DWORDS
        if (ll_read_finished) begin
          //deactivate FIFO
          d2h_data_stb      <=  1;
          cl_of_activate    <=  0;
          state             <=  IDLE;
          if (pio_response) begin
            //write the end status to the status pin
            d2h_status        <=  pio_e_status;
          end
        end
      end
      default: begin
        state               <=  IDLE;
      end
    endcase
    if (sync_escape) begin
      state                 <=  IDLE;
    end
  end
end


endmodule