// FIFO Interface to the 2K buffer RAMs
// Read port is read-acknowledge
// FIXME do we want to be able to interleave reads and writes?

module buffer_int
  #(parameter BUFF_NUM = 0)
    (// Control Interface
     input clk,
     input rst,
     input [31:0] ctrl_word,
     input go,
     output done,
     output error,
     output idle,
     
     // Buffer Interface
     output en_o,
     output we_o,
     output reg [8:0] addr_o,
     output [31:0] dat_to_buf,
     input [31:0] dat_from_buf,
     
     // Write FIFO Interface
     input [31:0] wr_dat_i,
     input wr_write_i,
     input wr_done_i,
     input wr_error_i,
     output reg wr_ready_o,
     output reg wr_full_o,
     
     // Read FIFO Interface
     output [31:0] rd_dat_o,
     input rd_read_i,
     input rd_done_i,
     input rd_error_i,
     output reg rd_sop_o,
     output reg rd_eop_o
     );
   
   reg [31:0] ctrl_reg;
   reg 	      go_reg;
   
   always @(posedge clk)
     go_reg <= go;
   
   always @(posedge clk)
     if(rst)
       ctrl_reg <= 0;
     else
       if(go & (ctrl_word[31:28] == BUFF_NUM))
	 ctrl_reg <= ctrl_word;
   
   wire [8:0] firstline = ctrl_reg[8:0];
   wire [8:0] lastline = ctrl_reg[17:9];
   wire [3:0] step = ctrl_reg[21:18];
   wire       read = ctrl_reg[22];
   wire       write = ctrl_reg[23];
   wire       clear = ctrl_reg[24];
   //wire [2:0] port = ctrl_reg[27:25];  // Ignored in this block
   //wire [3:0] buff_num = ctrl_reg[31:28];  // Ignored here ?
   
   assign     dat_to_buf = wr_dat_i;
   assign     rd_dat_o = dat_from_buf;
   
   localparam IDLE = 3'd0;
   localparam PRE_READ = 3'd1;
   localparam READING = 3'd2;
   localparam WRITING = 3'd3;
   localparam ERROR = 3'd4;
   localparam DONE = 3'd5;
   
   reg [2:0]  state;
   
   always @(posedge clk)
     if(rst)
       begin
	  state <= IDLE;
	  rd_sop_o <= 0;
	  rd_eop_o <= 0;
	  wr_ready_o <= 0;
	  wr_full_o <= 0;
       end
     else
       if(clear)
	 begin
	    state <= IDLE;
	    rd_sop_o <= 0;
	    rd_eop_o <= 0;
	    wr_ready_o <= 0;
	    wr_full_o <= 0;
	 end
       else 
	 case(state)
	   IDLE :
	     if(go_reg & read)
	       begin
		  addr_o <= firstline;
		  state <= PRE_READ;
	       end
	     else if(go_reg & write)
	       begin
		  addr_o <= firstline;
		  state <= WRITING;
		  wr_ready_o <= 1;
	       end
	   
	   PRE_READ :
	     begin
		state <= READING;
		addr_o <= addr_o + 1;
		rd_sop_o <= 1;
	     end
	   
	   READING :
	     if(rd_error_i)
	       state <= ERROR;
	     else if(rd_done_i)
	       state <= DONE;
	     else if(rd_read_i)
	       begin
		  rd_sop_o <= 0;
		  addr_o <= addr_o + 1;
		  if(addr_o == lastline)
		    rd_eop_o <= 1;
		  else
		    rd_eop_o <= 0;
		  if(rd_eop_o)
		    state <= DONE;
	       end
	   
	   WRITING :
	     begin
		if(wr_write_i)
		  addr_o <= addr_o + 1;  // This was the timing problem, so now it doesn't depend on wr_error_i
		if(wr_error_i)
		  begin
		     state <= ERROR;
		     wr_ready_o <= 0;
		  end
		else
		  begin
		     if(wr_write_i)
		       begin
			  wr_ready_o <= 0;
			  if(addr_o == (lastline-1))
			    wr_full_o <= 1;
			  if(addr_o == lastline)
			    state <= DONE;
		       end
		     if(wr_done_i)
		       begin
			  state <= DONE;
			  wr_ready_o <= 0;
		       end
		  end // else: !if(wr_error_i)
	     end // case: WRITING

	   DONE :
	     begin
		rd_eop_o <= 0;
		rd_sop_o <= 0;
		wr_ready_o <= 0;
		wr_full_o <= 0;
	     end
	   
	 endcase // case(state)
   
   // FIXME ignores step for now

   assign     we_o = (state == WRITING) && wr_write_i;  // FIXME potential critical path
                   // IF this is a timing problem, we could always write when in this state
   assign     en_o = ~((state==READING)& ~rd_read_i);   // FIXME potential critical path
   
   assign     done = (state == DONE);
   assign     error = (state == ERROR);
   assign     idle = (state == IDLE);
endmodule // buffer_int



// These are 2 other ways for doing the WRITING state, both work.  First one is faster, but confusing
/*
	     begin
		// Gen 4 values -- state, wr_ready_o, addr_o, wr_full_o
		if(~wr_error_i & wr_write_i & (addr_o == (lastline-1)))
		  wr_full_o <= 1;
		if(wr_error_i | wr_write_i | wr_done_i)
		  wr_ready_o <= 0;
		if(wr_error_i)
		  state <= ERROR;
		else if(wr_done_i | (wr_write_i & (addr_o == lastline)))
		  state <= DONE;
		// This one was the timing problem...  now we increment addr_o even if there is an error
		if(wr_write_i)
		  addr_o <= addr_o + 1;
	     end // case: WRITING
*/	   
		
/*      begin
		if(wr_error_i)
		  begin
		     state <= ERROR;
		     wr_ready_o <= 0;
		  end
		else
		  begin
		     if(wr_write_i)
		       begin
			  wr_ready_o <= 0;
			  addr_o <= addr_o + 1;
			  if(addr_o == (lastline-1))
			    wr_full_o <= 1;
			  if(addr_o == lastline)
			    state <= DONE;
		       end
		     if(wr_done_i)
		       begin
			  state <= DONE;
			  wr_ready_o <= 0;
		       end
		  end // else: !if(wr_error_i)
	     end // case: WRITING
*/














// Unused old code
   //assign     rd_empty_o = (state != READING); // && (state != PRE_READ);
   //assign     rd_empty_o = rd_empty_reg;         // timing fix?
   //assign     rd_ready_o = (state == READING);
   //assign     rd_ready_o = ~rd_empty_reg;        // timing fix?
   
   //wire       rd_en = (state == PRE_READ) || ((state == READING) && rd_read_i);
   //wire       wr_en = (state == WRITING) && wr_write_i;  // IF this is a timing problem, we could always enable when in this state
   //assign     en_o = rd_en | wr_en;   
   
   // assign     wr_full_o = (state != WRITING);
   // assign     wr_ready_o = (state == WRITING);