//
// AXI stream neds N+1 bits to transmit packets of N bits so that the LAST bit can be represented.
// LAST occurs relatively infrequently and can be synthesized by using an in-band ESC code to generate
// a multi-word sequence to encode it (and the escape character when it appears as data input).
//
// 0x1234567887654321 with last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0x1234567887654321
//
// 0xDEADBEEFFEEDCAFE with last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000001 0xDEADBEEFFEEDCAFE
//
// 0xDEADBEEFFEEDCAFE without last becomes
// 0xDEADBEEFFEEDCAFE 0x0000000000000000 0xDEADBEEFFEEDCAFE
//

module axi_extract_tlast
   #(parameter WIDTH=64)
  (
   input clk,
   input reset,
   input clear,
   //
   input [WIDTH-1:0] i_tdata,
   input i_tvalid,
   output reg i_tready,
   //
   output [WIDTH-1:0] o_tdata,
   output reg o_tlast,
   output reg o_tvalid,
   input  o_tready,
   //
   output reg checksum_error_reg
   
   );

   reg [1:0] state, next_state;
   
   localparam IDLE = 0;
   localparam EXTRACT1 = 1;
   localparam EXTRACT2 = 2;
   localparam EXTRACT3 = 3;
   
   assign     o_tdata = i_tdata;

   reg [31:0] checksum, old_checksum;
   reg       checksum_error;
   
   
   always @(posedge clk) 
     if (reset | clear) begin
	checksum <= 0;
	old_checksum <= 0;
     end else if (o_tready && i_tvalid && o_tlast) begin
	checksum <= 0;
	old_checksum <= 0;
     end else if (i_tready && i_tvalid && (state == IDLE)) begin
	checksum <= checksum + i_tdata[31:0] + i_tdata[63:32];
	old_checksum <= checksum;
     end

   always @(posedge clk)
     checksum_error_reg <= checksum_error;

   always @(posedge clk) 
     if (reset | clear) begin
	state <= IDLE;
     end else begin
	state <= next_state;
     end 

   always @(*) begin
      checksum_error = 0;
      case(state)
	//
	// Search for Escape sequence "0xDEADBEEFFEEDCAFE"
	// If ESC found don't pass data downstream but transition to next state.
	// else pass data downstream.
	//
	IDLE: begin
	   o_tlast = 1'b0;
	   if ((i_tdata == 64'hDEADBEEFFEEDCAFE) && i_tvalid)
	     begin
		next_state = EXTRACT1;
		o_tvalid = 1'b0;
		i_tready = 1'b1;
	     end
	   else
	     begin
		next_state = IDLE;
		o_tvalid = i_tvalid;
		i_tready = o_tready;
	     end // else: !if((i_tdata == 'hDEADBEEFFEEDCAFE) && i_tvalid)
	end // case: IDLE
	//
	// Look at next data. If it's a 0x1 then o_tlast should be asserted with next data word.
	// if it's 0x0 then it signals emulation of the Escape code in the original data stream
	// and we should just pass the next data word through unchanged with no o_tlast indication.
	//
	EXTRACT1: begin
	   o_tvalid = 1'b0;
	   i_tready = 1'b1;
	   o_tlast = 1'b0;
	   if (i_tvalid) begin
	      if (i_tdata[31:0] == 'h1)
		begin
		   if (old_checksum != i_tdata[63:32]) 
		     checksum_error = 1'b1;
		   next_state = EXTRACT2;
		end
	      else // We assume emulation and don't look for illegal codes.
		begin
		   next_state = EXTRACT3;
		end // else: !if(i_tdata == 'h1)
	   end else begin // if (i_tvalid)
	      next_state = EXTRACT1;
	   end // else: !if(i_tvalid)
	end // case: EXTRACT1
	//
	// Assert o_tlast with data word.
	//
	EXTRACT2: begin
	   o_tvalid = i_tvalid;
	   i_tready = o_tready;
	   o_tlast = 1'b1;
	   if (i_tvalid & o_tready)
	     next_state = IDLE;
	   else
	     next_state = EXTRACT2;
	end
	//
	// Emulation, don't assert o_tlast with dataword.
	//
	EXTRACT3: begin
	   o_tvalid = i_tvalid;
	   i_tready = o_tready;
	   o_tlast = 1'b0;
	   if (i_tvalid & o_tready)
	     next_state = IDLE;
	   else
	     next_state = EXTRACT2;
	end
      endcase // case(state)
   end
      


endmodule // axi_extract_tlast