module nt_gen_replay_assemble
(
  // clock and resets
  input wire          clk,
  input wire          rstn,
  input wire          rst_sw,

  // meta and packet data input FIFO
  input wire [63:0]   fifo_dout_i,
  output wire         fifo_rd_en_o,
  input wire          fifo_empty_i,

  // AXI stream master interface
  output reg [63:0]   m_axis_tdata,
  output reg          m_axis_tvalid,
  output reg          m_axis_tlast,
  output reg [7:0]    m_axis_tkeep,
  output reg [31:0]   m_axis_tuser,
  input wire          m_axis_tready,

  // control and status signals
  input wire          ctrl_start_i,
  input wire          status_mem_read_active_i,
  output wire         status_active_o
);

  parameter   IDLE            = 3'b000,
              START           = 3'b001,
              META            = 3'b010,
              TX              = 3'b011,
              WAIT            = 3'b100;

  reg [2:0] state, nxt_state;

  // per-packet meta data
  reg [31:0] meta_delta_t, nxt_meta_delta_t;
  reg [10:0] meta_len_wire, nxt_meta_len_wire;
  reg [10:0] meta_len_snap, nxt_meta_len_snap;

  // per-packet AXI4-Stream word counter
  reg [7:0] m_axis_word_cntr;

  // determine whether enough 64 bit wide AXI4-Stream words have been
  // transferred to fit the snap data in
  wire tx_done_snap;
  assign tx_done_snap = ((m_axis_word_cntr) << 3) >= meta_len_snap;

  // determine whether enough 64 bit wide AXI4-Stream words have been
  // transferred to fit the entire packet wire data in
  wire tx_done_wire;
  assign tx_done_wire = ((m_axis_word_cntr + 1) << 3) >= meta_len_wire;

  // input FIFO read enable
  assign fifo_rd_en_o = (state == META) |
    ((state == TX) & ~tx_done_snap & m_axis_tready);

  // data is being transmitted when we are in the TX state. If less than
  // snaplen bytes have been transferred, we must ensure that there is trace
  // data to read in the FIFO. if we are padding data with zero bytes we do
  // not need to read for data in the fifo to become ready.
  wire do_tx;
  assign do_tx =
    (state == TX) & (tx_done_snap | (~tx_done_snap & ~fifo_empty_i));

  // module is active whenever not in idle state
  assign status_active_o = (state != IDLE);

  // control fsm
  always @(posedge clk) begin
    if (~rstn | rst_sw) begin
      state <= IDLE;
    end else begin
      state <= nxt_state;
    end

    meta_delta_t <= nxt_meta_delta_t;
    meta_len_wire <= nxt_meta_len_wire;
    meta_len_snap <= nxt_meta_len_snap;
  end

  always @(*) begin
    nxt_state = state;

    nxt_meta_delta_t = meta_delta_t;
    nxt_meta_len_wire = meta_len_wire;
    nxt_meta_len_snap = meta_len_snap;

    case (state)

      IDLE: begin
        // leave idle state when module operation is started. ctrl_start_i is
        // expected to stay asserted for only a single clock cycle.
        if (ctrl_start_i) begin
          nxt_state = START;
        end
      end

      START: begin
        // we need to make sure that memory read module is active before we
        // start. thus, insert this dummy state here to delay start by one
        // cycle
        nxt_state = META;
      end

      META: begin
        if (fifo_empty_i & ~status_mem_read_active_i) begin
          // the input FIFO is empty and its not being filled with data from
          // the memory anymore. that means generation is done
          nxt_state = IDLE;
        end else if (~fifo_empty_i) begin
          if (fifo_dout_i == 64'hFFFFFFFFFFFFFFFF) begin
            // all bits of the meta data words are 1 -> this is padding data.
            // ignore data, don't do anything, stay in this state
          end else begin
            // FIFO output conatins meta data
            nxt_meta_delta_t = fifo_dout_i[31:0];
            nxt_meta_len_snap = fifo_dout_i[42:32];
            nxt_meta_len_wire = fifo_dout_i[58:48];

            // start packet transmission
            nxt_state = TX;
          end
        end
      end

      TX: begin
        if (tx_done_wire & m_axis_tready) begin
          // packet transmission done! start over for next packet
          nxt_state = META;
        end
      end

    endcase
  end

  // process applies data to output AXI4-Stream master interface and counts
  // the number of transferred words
  always @(posedge clk) begin
    if (~rstn | rst_sw) begin
      m_axis_tdata <= 64'b0;
      m_axis_tvalid <= 1'b0;
      m_axis_tlast <= 1'b0;
      m_axis_tkeep <= 8'b0;
      m_axis_tuser <= 32'b0;

      m_axis_word_cntr <= 8'b0;
    end else begin
      if (m_axis_tready) begin
        // slave is ready to receive data
        m_axis_tdata <= tx_done_snap ? 64'b0 : fifo_dout_i;
        m_axis_tvalid <= do_tx;
        m_axis_tlast <= do_tx & tx_done_wire;

        if (tx_done_wire) begin
          // last transaction for current packet, set strobe
          case (meta_len_wire[2:0] & 3'h7)
            3'h1: m_axis_tkeep <= 8'b00000001;
            3'h2: m_axis_tkeep <= 8'b00000011;
            3'h3: m_axis_tkeep <= 8'b00000111;
            3'h4: m_axis_tkeep <= 8'b00001111;
            3'h5: m_axis_tkeep <= 8'b00011111;
            3'h6: m_axis_tkeep <= 8'b00111111;
            3'h7: m_axis_tkeep <= 8'b01111111;
            3'h0: m_axis_tkeep <= 8'b11111111;
          endcase
        end else begin
          m_axis_tkeep <= 8'hFF;
        end

        // insert inter-packet transmission time in first TUSER word
        if (m_axis_word_cntr == 0) begin
          m_axis_tuser <= meta_delta_t;
        end else begin
          m_axis_tuser <= 32'b0;
        end

        if (do_tx) begin
          m_axis_word_cntr <= tx_done_wire ? 8'b0 : m_axis_word_cntr + 1;
        end else begin
          m_axis_word_cntr <= m_axis_word_cntr;
        end
      end else begin
        // slave not ready to receive data. keep output as it is.
        m_axis_tdata <= m_axis_tdata;
        m_axis_tvalid <= m_axis_tvalid;
        m_axis_tlast <= m_axis_tlast;
        m_axis_tkeep <= m_axis_tkeep;
        m_axis_tuser <= m_axis_tuser;

        m_axis_word_cntr <= m_axis_word_cntr;
      end
    end
  end

endmodule