module usb_fs_rx (
  // A 48MHz clock is required to recover the clock from the incoming data. 
  input clk_48mhz,
  input reset,

  // USB data+ and data- lines.
  input dp,
  input dn,

  // pulse on every bit transition.
  output bit_strobe,

  // Pulse on beginning of new packet.
  output pkt_start,

  // Pulse on end of current packet.
  output pkt_end,

  // Most recent packet decoded.
  output [3:0] pid,
  output reg [6:0] addr = 0,
  output reg [3:0] endp = 0,
  output reg [10:0] frame_num = 0,

  // Pulse on valid data on rx_data.
  output rx_data_put,
  output [7:0] rx_data,

  // Most recent packet passes PID and CRC checks
  output valid_packet
);
  wire clk = clk_48mhz;

  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////
  ////////
  //////// usb receive path
  ////////
  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////

  
  ////////////////////////////////////////////////////////////////////////////////
  // double flop for metastability
  /*
    all asynchronous inputs into the RTL need to be double-flopped to protect 
    against metastable scenarios.  if the RTL clock samples an asynchronous signal
    at the same time the signal is transitioning the result is undefined.  flopping
    the signal twice ensures it will be either 1 or 0 and nothing in between.
  */

  reg [3:0] dpair_q = 0;

  always @(posedge clk) begin
      dpair_q[3:0] <= {dpair_q[1:0], dp, dn};
  end


  ////////////////////////////////////////////////////////////////////////////////
  // line state recovery state machine
  /*
    the recieve path doesn't currently use a differential reciever.  because of
    this there is a chance that one of the differential pairs will appear to have
    changed to the new state while the other is still in the old state.  the 
    following state machine detects transitions and waits an extra sampling clock
    before decoding the state on the differential pair.  this transition period 
    will only ever last for one clock as long as there is no noise on the line.
    if there is enough noise on the line then the data may be corrupted and the
    packet will fail the data integrity checks.
  */

  reg [2:0] line_state = 0;
  localparam  DT = 3'b100;
  localparam  DJ = 3'b010;
  localparam  DK = 3'b001;
  localparam SE0 = 3'b000;
  localparam SE1 = 3'b011;

  wire [1:0] dpair = dpair_q[3:2];

  always @(posedge clk) begin
      case (line_state)
          // if we are in a transition state, then we can sample the pair and 
          // move to the next corresponding line state
          DT : begin
              case (dpair)
                  2'b10 : line_state <= DJ;
                  2'b01 : line_state <= DK;
                  2'b00 : line_state <= SE0;
                  2'b11 : line_state <= SE1;
              endcase
          end

          // if we are in a valid line state and the value of the pair changes,
          // then we need to move to the transition state
          DJ  : if (dpair != 2'b10) line_state <= DT;
          DK  : if (dpair != 2'b01) line_state <= DT;
          SE0 : if (dpair != 2'b00) line_state <= DT;
          SE1 : if (dpair != 2'b11) line_state <= DT;        

          // if we are in an invalid state we should move to the transition state
          default : line_state <= DT;
      endcase
  end


  ////////////////////////////////////////////////////////////////////////////////
  // clock recovery
  /*
    the DT state from the line state recovery state machine is used to align to 
    transmit clock.  the line state is sampled in the middle of the bit time.

    example of signal relationships
    -------------------------------
    line_state        DT  DJ  DJ  DJ  DT  DK  DK  DK  DK  DK  DK  DT  DJ  DJ  DJ
    line_state_valid  ________----____________----____________----________----____
    bit_phase         0   0   1   2   3   0   1   2   3   0   1   2   0   1   2
  */

  reg [1:0] bit_phase = 0;

  wire line_state_valid = (bit_phase == 1);
  assign bit_strobe = (bit_phase == 2);

  always @(posedge clk) begin
      // keep track of phase within each bit
      if (line_state == DT) begin
          bit_phase <= 0;

      end else begin
          bit_phase <= bit_phase + 1;
      end
  end


  ////////////////////////////////////////////////////////////////////////////////
  // packet detection 
  /*
    usb uses a sync to denote the beginning of a packet and two single-ended-0 to
    denote the end of a packet.  this state machine recognizes the beginning and
    end of packets for subsequent layers to process.
  */
  reg [5:0] line_history = 0;
  reg packet_valid = 0;
  reg next_packet_valid;
  wire packet_start = next_packet_valid && !packet_valid;
  wire packet_end = !next_packet_valid && packet_valid;

  always @* begin
    if (line_state_valid) begin
      // check for packet start: KJKJKK
      if (!packet_valid && line_history[5:0] == 6'b100101) begin
        next_packet_valid <= 1;
      end
 
      // check for packet end: SE0 SE0
      else if (packet_valid && line_history[3:0] == 4'b0000) begin
        next_packet_valid <= 0;

      end else begin
        next_packet_valid <= packet_valid;
      end
    end else begin
      next_packet_valid <= packet_valid;
    end
  end

  always @(posedge clk) begin
    if (reset) begin
      line_history <= 6'b101010;
      packet_valid <= 0;
    end else begin
      // keep a history of the last two states on the line
      if (line_state_valid) begin
        line_history[5:0] <= {line_history[3:0], line_state[1:0]};
      end
    end
    packet_valid <= next_packet_valid;
  end


  ////////////////////////////////////////////////////////////////////////////////
  // NRZI decode
  /*
    in order to ensure there are enough bit transitions for a receiver to recover
    the clock usb uses NRZI encoding.

    https://en.wikipedia.org/wiki/Non-return-to-zero
  */
  reg dvalid_raw;
  reg din;

  always @* begin
    case (line_history[3:0])
      4'b0101 : din <= 1;
      4'b0110 : din <= 0;
      4'b1001 : din <= 0;
      4'b1010 : din <= 1;
      default : din <= 0;
    endcase
 
    if (packet_valid && line_state_valid) begin
      case (line_history[3:0])
        4'b0101 : dvalid_raw <= 1;
        4'b0110 : dvalid_raw <= 1;
        4'b1001 : dvalid_raw <= 1;
        4'b1010 : dvalid_raw <= 1;
        default : dvalid_raw <= 0;
      endcase
    end else begin
      dvalid_raw <= 0;
    end
  end

  reg [5:0] bitstuff_history = 0;

  always @(posedge clk) begin
    if (reset || packet_end) begin
      bitstuff_history <= 6'b000000;
    end else begin
      if (dvalid_raw) begin
        bitstuff_history <= {bitstuff_history[4:0], din};
      end
    end  
  end

  wire dvalid = dvalid_raw && !(bitstuff_history == 6'b111111);


  ////////////////////////////////////////////////////////////////////////////////
  // save and check pid
  /*
    shift in the entire 8-bit pid with an additional 9th bit used as a sentinal.
  */

  reg [8:0] full_pid = 0;
  wire pid_valid = full_pid[4:1] == ~full_pid[8:5];
  wire pid_complete = full_pid[0];

  always @(posedge clk) begin
    if (packet_start) begin
      full_pid <= 9'b100000000;
    end

    if (dvalid && !pid_complete) begin
        full_pid <= {din, full_pid[8:1]};
    end
  end


  ////////////////////////////////////////////////////////////////////////////////
  // check crc5
  reg [4:0] crc5 = 0;
  wire crc5_valid = crc5 == 5'b01100;
  wire crc5_invert = din ^ crc5[4];
  always @(posedge clk) begin
    if (packet_start) begin
      crc5 <= 5'b11111;
    end

    if (dvalid && pid_complete) begin
      crc5[4] <= crc5[3];
      crc5[3] <= crc5[2];
      crc5[2] <= crc5[1] ^ crc5_invert;
      crc5[1] <= crc5[0];
      crc5[0] <= crc5_invert;
    end
  end


  ////////////////////////////////////////////////////////////////////////////////
  // check crc16
  reg [15:0] crc16 = 0;
  wire crc16_valid = crc16 == 16'b1000000000001101;
  wire crc16_invert = din ^ crc16[15];  

  always @(posedge clk) begin
    if (packet_start) begin
      crc16 <= 16'b1111111111111111;
    end

    if (dvalid && pid_complete) begin
      crc16[15] <= crc16[14] ^ crc16_invert;
      crc16[14] <= crc16[13];
      crc16[13] <= crc16[12];
      crc16[12] <= crc16[11];
      crc16[11] <= crc16[10];
      crc16[10] <= crc16[9];
      crc16[9] <= crc16[8];
      crc16[8] <= crc16[7];
      crc16[7] <= crc16[6];
      crc16[6] <= crc16[5];
      crc16[5] <= crc16[4];
      crc16[4] <= crc16[3];
      crc16[3] <= crc16[2];
      crc16[2] <= crc16[1] ^ crc16_invert;
      crc16[1] <= crc16[0];
      crc16[0] <= crc16_invert;
    end
  end


  ////////////////////////////////////////////////////////////////////////////////
  // output control signals
  wire pkt_is_token = full_pid[2:1] == 2'b01;
  wire pkt_is_data = full_pid[2:1] == 2'b11;
  wire pkt_is_handshake = full_pid[2:1] == 2'b10;


  // TODO: need to check for data packet babble
  // TODO: do i need to check for bitstuff error?
  assign valid_packet = pid_valid && (
    (pkt_is_handshake) || 
    (pkt_is_data && crc16_valid) ||
    (pkt_is_token && crc5_valid)
  );
  

  reg [11:0] token_payload = 0;
  wire token_payload_done = token_payload[0];

  always @(posedge clk) begin
    if (packet_start) begin
      token_payload <= 12'b100000000000;
    end

    if (dvalid && pid_complete && pkt_is_token && !token_payload_done) begin
      token_payload <= {din, token_payload[11:1]};
    end
  end

  always @(posedge clk) begin
    if (token_payload_done && pkt_is_token) begin
      addr <= token_payload[7:1];
      endp <= token_payload[11:8];
      frame_num <= token_payload[11:1];
    end
  end

  assign pkt_start = packet_start;
  assign pkt_end = packet_end; 
  assign pid = full_pid[4:1]; 
  //assign addr = token_payload[7:1];
  //assign endp = token_payload[11:8];
  //assign frame_num = token_payload[11:1];
  

  ////////////////////////////////////////////////////////////////////////////////
  // deserialize and output data
  //assign rx_data_put = dvalid && pid_complete && pkt_is_data;
  reg [8:0] rx_data_buffer = 0;
  wire rx_data_buffer_full = rx_data_buffer[0];
  assign rx_data_put = rx_data_buffer_full;
  assign rx_data = rx_data_buffer[8:1];

  always @(posedge clk) begin
    if (packet_start || rx_data_buffer_full) begin
      rx_data_buffer <= 9'b100000000;
    end

    if (dvalid && pid_complete && pkt_is_data) begin
        rx_data_buffer <= {din, rx_data_buffer[8:1]};
    end
  end

endmodule // usb_fs_rx
