module usb_fs_tx (
  // A 48MHz clock is required to receive USB data at 12MHz
  // it's simpler to juse use 48MHz everywhere
  input clk_48mhz,
  input reset,

  // bit strobe from rx to align with senders clock
  input bit_strobe,

  // output enable to take ownership of bus and data out
  output reg oe = 0,
  output reg dp = 0,
  output reg dn = 0,

  // pulse to initiate new packet transmission
  input pkt_start,
  output pkt_end,

  // pid to send
  input [3:0] pid,

  // tx logic pulls data until there is nothing available
  input tx_data_avail,  
  output reg tx_data_get = 0,
  input [7:0] tx_data
);
  wire clk = clk_48mhz;

  // save packet parameters at pkt_start
  reg [3:0] pidq = 0;

  always @(posedge clk) begin
    if (pkt_start) begin
      pidq <= pid;
    end
  end

  reg [7:0] data_shift_reg = 0;
  reg [7:0] oe_shift_reg = 0;
  reg [7:0] se0_shift_reg = 0;


  wire serial_tx_data = data_shift_reg[0];
  wire serial_tx_oe = oe_shift_reg[0];
  wire serial_tx_se0 = se0_shift_reg[0];


  // serialize sync, pid, data payload, and crc16
  reg byte_strobe = 0;
  reg [2:0] bit_count = 0;

  reg [4:0] bit_history_q = 0;
  wire [5:0] bit_history = {serial_tx_data, bit_history_q};
  wire bitstuff = bit_history == 6'b111111;
  reg bitstuff_q = 0;
  reg bitstuff_qq = 0;
  reg bitstuff_qqq = 0;
  reg bitstuff_qqqq = 0;


  always @(posedge clk) begin
    bitstuff_q <= bitstuff;
    bitstuff_qq <= bitstuff_q;
    bitstuff_qqq <= bitstuff_qq;
    bitstuff_qqqq <= bitstuff_qqq;
  end

  assign pkt_end = bit_strobe && se0_shift_reg[1:0] == 2'b01;

  reg data_payload = 0;

  reg [31:0] pkt_state = 0;
  localparam IDLE = 0;
  localparam SYNC = 1;
  localparam PID = 2;
  localparam DATA_OR_CRC16_0 = 3;
  localparam CRC16_1 = 4;
  localparam EOP = 5;

  reg [15:0] crc16 = 0;

  always @(posedge clk) begin
    case (pkt_state)
      IDLE : begin
        if (pkt_start) begin
          pkt_state <= SYNC;
        end
      end

      SYNC : begin
        if (byte_strobe) begin
          pkt_state <= PID;
          data_shift_reg <= 8'b10000000;
          oe_shift_reg <= 8'b11111111;
          se0_shift_reg <= 8'b00000000;
        end
      end

      PID : begin
        if (byte_strobe) begin
          if (pidq[1:0] == 2'b11) begin
            pkt_state <= DATA_OR_CRC16_0;
          end else begin
            pkt_state <= EOP;
          end

          data_shift_reg <= {~pidq, pidq};
          oe_shift_reg <= 8'b11111111;
          se0_shift_reg <= 8'b00000000;
        end
      end

      DATA_OR_CRC16_0 : begin
        if (byte_strobe) begin
          if (tx_data_avail) begin
            pkt_state <= DATA_OR_CRC16_0;
            data_payload <= 1;
            tx_data_get <= 1;
            data_shift_reg <= tx_data;
            oe_shift_reg <= 8'b11111111;
            se0_shift_reg <= 8'b00000000;
          end else begin
            pkt_state <= CRC16_1;
            data_payload <= 0;
            tx_data_get <= 0;
            data_shift_reg <= ~{crc16[8], crc16[9], crc16[10], crc16[11], crc16[12], crc16[13], crc16[14], crc16[15]};
            oe_shift_reg <= 8'b11111111;
            se0_shift_reg <= 8'b00000000;
          end
        end else begin
          tx_data_get <= 0; 
        end
      end

      CRC16_1 : begin
        if (byte_strobe) begin
          pkt_state <= EOP;
          data_shift_reg <= ~{crc16[0], crc16[1], crc16[2], crc16[3], crc16[4], crc16[5], crc16[6], crc16[7]};
          oe_shift_reg <= 8'b11111111;
          se0_shift_reg <= 8'b00000000;
        end
      end

      EOP : begin
        if (byte_strobe) begin
          pkt_state <= IDLE;
          oe_shift_reg <= 8'b00000111;
          se0_shift_reg <= 8'b00000111;
        end
      end
    endcase

    if (bit_strobe && !bitstuff) begin
      byte_strobe <= (bit_count == 3'b000);
    end else begin
      byte_strobe <= 0;
    end

    if (pkt_start) begin
      bit_count <= 1;
      bit_history_q <= 0;

    end else if (bit_strobe) begin
      // bitstuff
      if (bitstuff /* && !serial_tx_se0*/) begin
        bit_history_q <= bit_history[5:1];
        data_shift_reg[0] <= 0;

      // normal deserialize
      end else begin
        bit_count <= bit_count + 1;

        data_shift_reg <= (data_shift_reg >> 1);
        oe_shift_reg <= (oe_shift_reg >> 1);
        se0_shift_reg <= (se0_shift_reg >> 1);

        bit_history_q <= bit_history[5:1];
      end
    end
  end



  // calculate crc16
  wire crc16_invert = serial_tx_data ^ crc16[15];  

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

    if (bit_strobe && data_payload && !bitstuff_qqqq && !pkt_start) 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

  reg [2:0] dp_eop = 0;


  // nrzi and differential driving
  always @(posedge clk) begin
    if (pkt_start) begin
      // J
      dp <= 1;
      dn <= 0;
      
      dp_eop <= 3'b100;

    end else if (bit_strobe) begin
      oe <= serial_tx_oe;

      if (serial_tx_se0) begin
        dp <= dp_eop[0];
        dn <= 0;

        dp_eop <= dp_eop >> 1;

      end else if (serial_tx_data) begin
        // value should stay the same, do nothing

      end else begin
        dp <= !dp;
        dn <= !dn;
      end
    end
  end 

endmodule