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

  // AXI4-Stream slave interface
  input wire [63:0]   s_axis_tdata,
  input wire          s_axis_tvalid,
  input wire          s_axis_tlast,
  input wire [7:0]    s_axis_tkeep,
  input wire [52:0]   s_axis_tuser,
  output reg          s_axis_tready,

  // activation signals
  input wire          active_i,
  input wire [15:0]   max_len_capture_i,

  // fifo signals for meta-data fifo
  output reg [74:0]   fifo_meta_din_o,
  output reg          fifo_meta_wr_en_o,
  input wire          fifo_meta_full_i,

  // fifo signals for data fifo
  output reg [63:0]   fifo_data_din_o,
  output reg          fifo_data_wr_en_o,
  input wire          fifo_data_full_i,

  // module status output
  output reg          active_o,

  // packet counter output
  output reg [31:0]   pkt_cnt_o,

  // fifo full error output
  output reg          err_meta_fifo_full_o,
  output reg          err_data_fifo_full_o
);

  // module is always ready to receive new data!
  always @(posedge clk) s_axis_tready <= 1'b1;

  // register stores whether there is a packet transmission currently active
  // on the AXI4-Stream interface
  reg s_axis_active;

  // process determines whether there is currently a packet transmission active
  // on the AXI4-Stream interface
  always @(posedge clk) begin
    if (~rstn | rst_sw) begin
      // initially no transmission active
      s_axis_active <= 1'b0;
    end else begin
      if (s_axis_tvalid & s_axis_tready) begin
        // transmission of packet done when tlast signal is asserted
        s_axis_active <= ~s_axis_tlast;
      end else begin
        // no data word is transfered in this cycle
        s_axis_active <= s_axis_active;
      end
    end
  end

  // calculate number of bytes that are valid in the current AXI4-Stream word
  reg [3:0] s_axis_tdata_len;

  integer idx;
  always @(*) begin
    if (~s_axis_tlast) begin
      // not the last word -> all 8 bytes valid
      s_axis_tdata_len = 8;
    end else begin
      // last word -> number of 1s in TKEEP signal determines number of valid
      // bytes
      s_axis_tdata_len = 4'b0;
      for (idx = 0; idx < 8; idx = idx + 1) begin
        s_axis_tdata_len = s_axis_tdata_len + s_axis_tkeep[idx];
      end
    end
  end

  // states of FSM writing data
  parameter FSM_DATA_RST    = 3'b000,
            FSM_DATA_IDLE   = 3'b001,
            FSM_DATA_START  = 3'b010,
            FSM_DATA_RECORD = 3'b011,
            FSM_DATA_ERR    = 3'b100;

  reg [2:0]   state_fsm_data, nxt_state_fsm_data;
  reg         meta_wr, nxt_meta_wr;
  reg [23:0]  meta_latency, nxt_meta_latency;
  reg         meta_latency_valid, nxt_meta_latency_valid;
  reg [27:0]  meta_interpackettime, nxt_meta_interpackettime;
  reg [10:0]  meta_len_capture, nxt_meta_len_capture;
  reg [10:0]  meta_len_wire, nxt_meta_len_wire;
  reg [10:0]  len_capture, nxt_len_capture;
  reg [10:0]  len_wire, nxt_len_wire;
  reg         nxt_fifo_data_wr_en;
  reg [31:0]  nxt_pkt_cnt;

  // FSM writing packet data to FIFO
  always @(posedge clk) begin
    if (~rstn | rst_sw) begin
      state_fsm_data <= FSM_DATA_RST;
    end else begin
      state_fsm_data <= nxt_state_fsm_data;
    end

    meta_wr <= nxt_meta_wr;
    meta_latency <= nxt_meta_latency;
    meta_latency_valid <= nxt_meta_latency_valid;
    meta_interpackettime <= nxt_meta_interpackettime;
    meta_len_capture <= nxt_meta_len_capture;
    meta_len_wire <= nxt_meta_len_wire;
    len_capture <= nxt_len_capture;
    len_wire <= nxt_len_wire;
    fifo_data_wr_en_o <= nxt_fifo_data_wr_en;
    fifo_data_din_o <= s_axis_tdata;
    pkt_cnt_o <= nxt_pkt_cnt;
  end

  // FSM writing packet data to FIFO
  always @(*) begin
    nxt_state_fsm_data = state_fsm_data;
    nxt_meta_wr = 1'b0;
    nxt_meta_latency = meta_latency;
    nxt_meta_latency_valid = meta_latency_valid;
    nxt_meta_interpackettime = meta_interpackettime;
    nxt_meta_len_capture = meta_len_capture;
    nxt_meta_len_wire = meta_len_wire;
    nxt_len_capture = len_capture;
    nxt_len_wire = len_wire;
    nxt_fifo_data_wr_en = 1'b0;
    nxt_pkt_cnt = pkt_cnt_o;

    case (state_fsm_data)

      FSM_DATA_RST: begin
        // reset packet counter
        nxt_pkt_cnt = 32'b0;

        // go to idle state
        nxt_state_fsm_data = FSM_DATA_IDLE;
      end

      FSM_DATA_IDLE: begin
        if (active_i) begin
          // module has been activated
          nxt_state_fsm_data = FSM_DATA_START;
        end
      end

      FSM_DATA_START: begin
        // reset per-packet length counters
        nxt_len_capture = 11'b0;
        nxt_len_wire = 11'b0;

        // reset packet counter
        nxt_pkt_cnt = 32'b0;

        // to prevent incomplete packet data to bet written to the FIFO, wait
        // in this state until there is no packet transmission active on the
        // axi stream slave interface anymore.
        if (~s_axis_active) begin
          nxt_state_fsm_data = FSM_DATA_RECORD;
        end
      end

      FSM_DATA_RECORD: begin
        if (~active_i & ~s_axis_active) begin
          // module is being deactivated and there is currently no packet
          // transmission active anymore -> go back to idle state
          nxt_state_fsm_data = FSM_DATA_IDLE;
        end else begin
          if (s_axis_tvalid & s_axis_tready) begin
            // AXI4-Stream word is being transfered

            if (len_capture < max_len_capture_i) begin
              // maximum capture length not reached yet -> store data
              nxt_fifo_data_wr_en = 1'b1;

              if (fifo_data_full_i) begin
                // FIFO is full -> go to error state_fsm_data
                nxt_state_fsm_data = FSM_DATA_ERR;
              end
            end

            if (s_axis_tlast) begin
              // this is the last word -> trigger writing of meta data
              nxt_meta_wr = 1'b1;

              // set meta data
              nxt_meta_latency = s_axis_tuser[23:0];
              nxt_meta_latency_valid = s_axis_tuser[24:24];
              nxt_meta_interpackettime = s_axis_tuser[52:25];
              if ((len_capture + s_axis_tdata_len) > max_len_capture_i) begin
                nxt_meta_len_capture = max_len_capture_i;
              end else begin
                nxt_meta_len_capture = len_capture + s_axis_tdata_len;
              end
              nxt_meta_len_wire = len_wire + s_axis_tdata_len;

              // increment packet counter
              nxt_pkt_cnt = pkt_cnt_o + 1;

              // reset packet length counters
              nxt_len_capture = 11'b0;
              nxt_len_wire = 11'b0;
            end else begin
              // not the last word yet, increment counters
              if ((len_capture + s_axis_tdata_len) > max_len_capture_i) begin
                nxt_len_capture = max_len_capture_i;
              end else begin
                nxt_len_capture = len_capture + s_axis_tdata_len;
              end
              nxt_len_wire = len_wire + s_axis_tdata_len;
            end
          end
        end
      end

      FSM_DATA_ERR: begin
        // stuck here until reset
      end

    endcase
  end

  // states of FSM writing meta info
  parameter FSM_META_ACTIVE = 1'b0,
            FSM_META_ERR    = 1'b1;

  reg         state_fsm_meta, nxt_state_fsm_meta;
  reg         nxt_fifo_meta_wr_en;
  reg [74:0]  nxt_fifo_meta_din;

  // FSM writing meta data to FIFO
  always @(posedge clk) begin
    if (~rstn | rst_sw) begin
      state_fsm_meta <= FSM_META_ACTIVE;
    end else begin
      state_fsm_meta <= nxt_state_fsm_meta;
    end

    fifo_meta_wr_en_o <= nxt_fifo_meta_wr_en;
    fifo_meta_din_o <= nxt_fifo_meta_din;
  end

  // FSM writing meta data to FIFO
  always @(*) begin
    nxt_state_fsm_meta = state_fsm_meta;
    nxt_fifo_meta_wr_en = 1'b0;
    nxt_fifo_meta_din = fifo_meta_din_o;

    case (state_fsm_meta)

      FSM_META_ACTIVE: begin
        if (meta_wr) begin
          // assmeble meta data and write to FIFO
          nxt_fifo_meta_wr_en = 1'b1;
          nxt_fifo_meta_din[23:0] = meta_latency;
          nxt_fifo_meta_din[24:24] = meta_latency_valid;
          nxt_fifo_meta_din[52:25] = meta_interpackettime;
          nxt_fifo_meta_din[63:53] = meta_len_wire;
          nxt_fifo_meta_din[74:64] = meta_len_capture;

          if (fifo_meta_full_i) begin
            // fifo is full -> go to error state
            nxt_state_fsm_meta = FSM_META_ERR;
          end
        end
      end

      FSM_META_ERR: begin
        // stuck here until reset
      end

    endcase
  end

  // process sets status and error output signals
  always @(posedge clk) begin
    if (~rstn | rst_sw) begin
      active_o <= 1'b0;
      err_meta_fifo_full_o <= 1'b0;
      err_data_fifo_full_o <= 1'b0;
    end else begin
      active_o <= (state_fsm_data == FSM_DATA_RECORD);
      err_data_fifo_full_o <= (state_fsm_data == FSM_DATA_ERR);
      err_meta_fifo_full_o <= (state_fsm_meta == FSM_META_ERR);
    end
  end

endmodule