`define DSP_CORE_RX_BASE 160

module rx_control
  #(parameter FIFOSIZE = 10)
    (input clk, input rst,
     input set_stb, input [7:0] set_addr, input [31:0] set_data,
     
     input [31:0] master_time,
     output overrun,
     
     // To Buffer interface
     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,
     
     // From DSP Core
     input [31:0] sample,
     output run,
     input strobe,

     // FIFO Levels
     output [15:0] fifo_occupied,
     output fifo_full,
     output fifo_empty,
     
     // Debug
     output [31:0] debug_rx
     );

   wire [31:0] 	   new_time, new_command;
   wire 	   sc_pre1, clear_overrun;
   wire [31:0] 	   rcvtime_pre;
   reg [31:0] 	   rcvtime;
   wire [8:0] 	   lines_per_frame;
   wire [20:0] 	   numlines;
   wire 	   send_imm_pre, chain_pre;
   reg 		   send_imm, chain;
   wire 	   full_ctrl, read_ctrl, empty_ctrl, write_ctrl;

   setting_reg #(.my_addr(`DSP_CORE_RX_BASE+3)) sr_3
     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
      .in(set_data),.out(new_time),.changed(sc_pre1));
   
   setting_reg #(.my_addr(`DSP_CORE_RX_BASE+4)) sr_4
     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
      .in(set_data),.out(new_command),.changed());

   setting_reg #(.my_addr(`DSP_CORE_RX_BASE+5)) sr_5
     (.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
      .in(set_data),.out(),.changed(clear_overrun));

   reg 	       sc_pre2;
   always @(posedge clk)
     sc_pre2 <= sc_pre1;
   assign      write_ctrl = sc_pre1 & ~sc_pre2;
   
   shortfifo #(.WIDTH(64)) commandfifo
     (.clk(clk),.rst(rst),.clear(clear_overrun),
      .datain({new_command,new_time}), .write(write_ctrl), .full(full_ctrl),
      .dataout({send_imm_pre,chain_pre,numlines,lines_per_frame,rcvtime_pre}), 
      .read(read_ctrl), .empty(empty_ctrl) );

   // Buffer interface to internal FIFO
   wire    write, full, read, empty;
   wire    sop_o, eop_o;

   reg 	   xfer_state;
   localparam XFER_IDLE = 1'b0;
   localparam XFER_GO = 1'b1;

   always @(posedge clk)
     if(rst)
       xfer_state <= XFER_IDLE;
     else
       if(clear_overrun)
	 xfer_state <= XFER_IDLE;
       else
	 case(xfer_state)
	   XFER_IDLE :
	     if(wr_ready_i)
	       xfer_state <= XFER_GO;
	   XFER_GO :
	     if((eop_o | wr_full_i) & wr_write_o)
	       xfer_state <= XFER_IDLE;
	   default :
	     xfer_state <= XFER_IDLE;
	 endcase // case(xfer_state)
   
   assign     wr_write_o = (xfer_state == XFER_GO) & ~empty;
   assign     wr_done_o = (eop_o & wr_write_o);
   assign     wr_error_o = 0;   // FIXME add check here for eop if we have wr_full_i once we have IBS

   assign     read = wr_write_o | (~empty & ~sop_o);   // FIXME  what if there is junk between packets?

   wire [33:0] fifo_line;
   
   // Internal FIFO, size 9 is 2K, size 10 is 4K
   cascadefifo2 #(.WIDTH(34),.SIZE(FIFOSIZE)) rxfifo
     (.clk(clk),.rst(rst),.clear(clear_overrun),
      .datain(fifo_line), .write(write), .full(full),
      .dataout({sop_o,eop_o,wr_dat_o}), .read(read), .empty(empty),
      .space(),.occupied(fifo_occupied) );
   assign      fifo_full = full;
   assign      fifo_empty = empty;

   // Internal FIFO to DSP interface
   reg [22:0] lines_left;
   reg [8:0]  lines_left_frame;
   localparam IBS_IDLE = 0;
   localparam IBS_WAITING = 1;
   localparam IBS_FIRSTLINE = 2;
   localparam IBS_RUNNING = 3;
   localparam IBS_OVERRUN = 4;
   
   reg [2:0] ibs_state;
  
   wire [32:0] delta_time = {1'b0,rcvtime}-{1'b0,master_time};
   wire        too_late = (delta_time[32:31] == 2'b11) & ~send_imm;
   wire        go_now = send_imm | ( master_time == rcvtime );
 
   always @(posedge clk)
     if(rst)
       begin
	  ibs_state <= IBS_IDLE;
	  lines_left <= 0;
	  lines_left_frame <= 0;
	  rcvtime <= 0;
	  send_imm <= 0;
	  chain <= 0;
       end
     else
       if(clear_overrun)
	 begin
	  ibs_state <= IBS_IDLE;
	  lines_left <= 0;
	  lines_left_frame <= 0;
	  rcvtime <= 0;
	  send_imm <= 0;
	  chain <= 0;
	 end
       else 
	 case(ibs_state)
	   IBS_IDLE :
	     if(~empty_ctrl)
	       begin
		  lines_left <= numlines;
		  lines_left_frame <= lines_per_frame;
		  rcvtime <= rcvtime_pre;
		  ibs_state <= IBS_WAITING;
		  send_imm <= send_imm_pre;
		  chain <= chain_pre;
	       end
	   IBS_WAITING :
	     if(go_now)
	       ibs_state <= IBS_FIRSTLINE;
	     else if(too_late)
	       ibs_state <= IBS_OVERRUN;
	   IBS_FIRSTLINE :
	     if(full | strobe)
	       ibs_state <= IBS_OVERRUN;
	     else
	       ibs_state <= IBS_RUNNING;
	   IBS_RUNNING :
	     if(strobe)
	       if(full)
		 ibs_state <= IBS_OVERRUN;
	       else
		 begin
		    lines_left <= lines_left - 1;
		    if(lines_left == 1)
		      if(~chain)
			ibs_state <= IBS_IDLE;
		      else if(empty_ctrl)
			ibs_state <= IBS_OVERRUN;
		      else
			begin
			   lines_left <= numlines;
			   lines_left_frame <= lines_per_frame;
			   rcvtime <= rcvtime_pre;
			   ibs_state <= IBS_FIRSTLINE;
			   send_imm <= send_imm_pre;
			   chain <= chain_pre;
			end
		    else if(lines_left_frame == 1)
		      begin
			 lines_left_frame <= lines_per_frame;
			 ibs_state <= IBS_FIRSTLINE;
		      end
		    else
		      lines_left_frame <= lines_left_frame - 1;
		 end // else: !if(full)
	 endcase // case(ibs_state)
   
   assign fifo_line = (ibs_state == IBS_FIRSTLINE) ? {1'b1,1'b0,master_time} :
		      {1'b0,((lines_left==1)|(lines_left_frame==1)),sample};
   
   assign write = ((ibs_state == IBS_FIRSTLINE) | strobe) & ~full;  // & (ibs_state == IBS_RUNNING) should strobe only when running
   assign overrun = (ibs_state == IBS_OVERRUN);
   assign run = (ibs_state == IBS_RUNNING) | (ibs_state == IBS_FIRSTLINE);
   assign read_ctrl = ( (ibs_state == IBS_IDLE) | 
			((ibs_state == IBS_RUNNING) & strobe & ~full & (lines_left==1) & chain) )
	  & ~empty_ctrl;
   
   assign debug_rx = { 6'd0,send_imm,chain,
		       wr_write_o, wr_done_o, wr_ready_i, wr_full_i,xfer_state,eop_o, sop_o, run,
		       write,full,read,empty,write_ctrl,full_ctrl,read_ctrl,empty_ctrl,
		       sc_pre1, clear_overrun, go_now, too_late, overrun, ibs_state[2:0] };
endmodule // rx_control