//
// Copyright 2011 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//


// 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 BUF_NUM = 0,
    parameter BUF_SIZE = 9)
    (// 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 [BUF_SIZE-1:0] addr_o,
     output [31:0] dat_to_buf,
     input [31:0] dat_from_buf,
     
     // Write FIFO Interface
     input [31:0] wr_data_i,
     input [3:0] wr_flags_i,
     input wr_ready_i,
     output wr_ready_o,
     
     // Read FIFO Interface
     output [31:0] rd_data_o,
     output [3:0] rd_flags_o,
     output rd_ready_o,
     input rd_ready_i
     );
   
   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] == BUF_NUM))
	 ctrl_reg <= ctrl_word;
   
   wire [BUF_SIZE-1:0] firstline = ctrl_reg[BUF_SIZE-1:0];
   wire [BUF_SIZE-1:0] lastline = ctrl_reg[2*BUF_SIZE-1:BUF_SIZE];

   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 ?
   
   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;
   reg 	      rd_sop, rd_eop;
   wire       wr_sop, wr_eop, wr_error;
   reg [1:0]  rd_occ;
   wire [1:0] wr_occ;
   
   always @(posedge clk)
     if(rst)
       begin
	  state <= IDLE;
	  rd_sop <= 0;
	  rd_eop <= 0;
	  rd_occ <= 0;
       end
     else
       if(clear)
	 begin
	    state <= IDLE;
	    rd_sop <= 0;
	    rd_eop <= 0;
	    rd_occ <= 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;
	       end
	   
	   PRE_READ :
	     begin
		state <= READING;
		addr_o <= addr_o + 1;
		rd_occ <= 2'b00;
		rd_sop <= 1;
		rd_eop <= 0;
	     end
	   
	   READING :
	     if(rd_ready_i)
	       begin
		  rd_sop <= 0;
		  addr_o <= addr_o + 1;
		  if(addr_o == lastline)
		    begin
		       rd_eop <= 1;
		       // FIXME assign occ here
		       rd_occ <= 0;
		    end
		  else
		    rd_eop <= 0;
		  if(rd_eop)
		    state <= DONE;
	       end
	   
	   WRITING :
	     begin
		if(wr_ready_i)
		  begin
		     addr_o <= addr_o + 1;
		     if(wr_error)
		       begin
			  state <= ERROR;
			  // Save OCC flags here
		       end
		     else if((addr_o == lastline)||wr_eop)
		       state <= DONE;
		  end // if (wr_ready_i)
	     end // case: WRITING
	   
	 endcase // case(state)
   
   assign     dat_to_buf = wr_data_i;
   assign     rd_data_o = dat_from_buf;

   assign     rd_flags_o = { rd_occ[1:0], rd_eop, rd_sop };
   assign     rd_ready_o = (state == READING);
   
   assign     wr_sop = wr_flags_i[0];
   assign     wr_eop = wr_flags_i[1];
   assign     wr_occ = wr_flags_i[3:2];
   assign     wr_error = wr_sop & wr_eop;
   assign     wr_ready_o = (state == WRITING);

   assign     we_o = (state == WRITING);
   //assign     we_o = (state == WRITING) && wr_ready_i;  // always write to avoid timing issue

   assign     en_o = ~((state==READING)& ~rd_ready_i);   // FIXME potential critical path
   
   assign     done = (state == DONE);
   assign     error = (state == ERROR);
   assign     idle = (state == IDLE);

endmodule // buffer_int
