module rx_prot_engine
  #(parameter FIFO_SIZE=11)
    (input clk, input rst,
     
     input Rx_mac_ra,
     output Rx_mac_rd,
     input [31:0] Rx_mac_data,
     input [1:0] Rx_mac_BE,
     input Rx_mac_pa,
     input Rx_mac_sop,
     input Rx_mac_eop,
     input Rx_mac_err,
     
     output [31:0] wr_dat_o,
     output wr_write_o,
     output wr_done_o,
     output wr_error_o,
     input wr_ready_i,
     input wr_full_i,
     output wr_flag_o,

     input set_stb,
     input [7:0] set_addr,
     input [31:0] set_data,
     
     output [15:0] rx_fifo_status,
     output reg [7:0] rx_seqnum,
     output reg [7:0] rx_channel,
     output [7:0] rx_flags
     );

   wire 	  read, write, full, empty;
   wire 	  eop_i, err_i, eop_o, err_o, flag_i, sop_i, flag_o, sop_o;
   wire [31:0] 	  dat_i, dat_o;
   reg 		  xfer_active;

   wire [3:0] 	  hdr_adr;
   wire [31:0] 	  hdr_dat;
   
   header_ram #(.REGNUM(48),.WIDTH(32)) rx_header_ram
     (.clk(clk),.set_stb(set_stb),.set_addr(set_addr),.set_data(set_data),
      .addr(hdr_adr),.q(hdr_dat));

   // Buffer interface side
   always @(posedge clk)
     if(rst)
       xfer_active <= 0;
     else if(wr_ready_i & ~empty)
       xfer_active <= 1;
     else if(eop_o | err_o | wr_full_i)
       xfer_active <= 0;

   assign wr_done_o = eop_o & wr_write_o;
   assign wr_error_o = err_o & wr_write_o;
   assign wr_dat_o = dat_o;
   assign wr_write_o = xfer_active & ~empty;
   assign read = wr_write_o;

   // FIFO in the middle
   cascadefifo2 #(.WIDTH(36),.SIZE(11)) rx_prot_fifo
     (.clk(clk),.rst(rst),
      .datain({flag_i,sop_i,eop_i,err_i,dat_i}),.write(write),.full(full),
      .dataout({flag_o,sop_o,eop_o,err_o,dat_o}),.read(read),.empty(empty),
      .clear(0),.fifo_space(rx_fifo_status));

   // MAC side
   localparam ETH_TYPE = 16'hBEEF;
   
   reg [2:0] prot_state;
   localparam PROT_IDLE = 0;
   localparam PROT_HDR1 = 1;
   localparam PROT_HDR2 = 2;
   localparam PROT_HDR3 = 3;
   localparam PROT_HDR4 = 4;
   localparam PROT_HDR5 = 5;
   localparam PROT_PKT = 6;

   // Things to control: flag_i, sop_i, eop_i, err_i, dat_i, write, Rx_mac_rd
   // Inputs to SM: Rx_mac_sop, Rx_mac_eop, Rx_mac_ra, Rx_mac_pa, 
   //                Rx_mac_BE, Rx_mac_err, full

   reg 	      flag;
   assign     dat_i = Rx_mac_data;
   assign     sop_i = Rx_mac_sop;
   assign     eop_i = Rx_mac_eop;
   assign     err_i = Rx_mac_err;
   assign     flag_i = flag;
   assign     wr_flag_o = flag_o;
   assign     Rx_mac_rd = (prot_state != PROT_IDLE) && (~full|~Rx_mac_pa);
   assign     write = (prot_state != PROT_IDLE) && ~full && Rx_mac_pa;	      

   assign     hdr_adr = {1'b0,prot_state[2:0]};

   wire [7:0] rx_seqnum_p1 = rx_seqnum + 1;
   
   always @(posedge clk)
     if(rst)
       begin
	  prot_state <= PROT_IDLE;
	  flag <= 0;
       end
     else if(prot_state == PROT_IDLE)
       begin
	  flag <= 0;
	  if(Rx_mac_ra)
	    prot_state <= PROT_HDR1;
       end
     else if(write)
       case(prot_state)
	 PROT_HDR1 : 
	   begin
	      prot_state <= PROT_HDR2;
	      if(hdr_dat != Rx_mac_data)
		flag <= 1;
	   end
	 PROT_HDR2 :
	   begin
	      prot_state <= PROT_HDR3;
	      if(hdr_dat != Rx_mac_data)
		flag <= 1;
	   end
	 PROT_HDR3 :
	   begin
	      prot_state <= PROT_HDR4;
	      if(hdr_dat != Rx_mac_data)
		flag <= 1;
	   end
	 PROT_HDR4 : 
	   begin
	      prot_state <= PROT_HDR5;
	      if(hdr_dat[31:16] != Rx_mac_data[31:16])
		flag <= 1;
	      rx_channel <= hdr_dat[15:8];
	   end
	 PROT_HDR5 :
	   begin
	      prot_state <= PROT_PKT;
	      if((rx_seqnum_p1) != Rx_mac_data[15:8])
		flag <= 1;
	   end
	 PROT_PKT : 
	   if(Rx_mac_eop | Rx_mac_err)
	     prot_state <= PROT_IDLE;
       endcase // case(prot_state)
   
   always @(posedge clk)
     if(rst)
       rx_seqnum <= 8'hFF;
     else if(set_stb & (set_addr == 54))
       rx_seqnum <= set_data[7:0];
     else if(write & (prot_state == PROT_HDR5) & ((rx_seqnum_p1) == Rx_mac_data[15:8]) & ~flag)
       rx_seqnum <= rx_seqnum + 1;
   
   // Error cases -- Rx_mac_error, BE != 0
endmodule // rx_prot_engine