// -*- verilog -*-
//
//  USRP - Universal Software Radio Peripheral
//
//  Copyright (C) 2003 Matt Ettus
//
//  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 2 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, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Boston, MA  02110-1301  USA
//

// Interface to Cypress FX2 bus
// A packet is 512 Bytes, the fifo has 4096 lines of 18 bits each

`include "../common/fpga_regs_common.v"
`include "../common/fpga_regs_standard.v"

module rx_buffer
  ( // Read/USB side
    input usbclk,
    input bus_reset,
    output [15:0] usbdata,
    input RD,
    output reg have_pkt_rdy,
    output reg rx_overrun,
    input clear_status,
    // Write/DSP side
    input rxclk,
    input reset,  // DSP side reset (used here), do not reset registers
    input rxstrobe,
    input wire [3:0] channels,
    input wire [15:0] ch_0,
    input wire [15:0] ch_1,
    input wire [15:0] ch_2,
    input wire [15:0] ch_3,
    input wire [15:0] ch_4,
    input wire [15:0] ch_5,
    input wire [15:0] ch_6,
    input wire [15:0] ch_7,
    // Settings, on rxclk also
    input [6:0] serial_addr, input [31:0] serial_data, input serial_strobe,
    input reset_regs, //Only reset registers
    output [31:0] debugbus
    );
   
   wire [15:0] 	  fifodata, fifodata_8;
   reg [15:0] 	  fifodata_16;
   
   wire [11:0] 	  rxfifolevel;
   wire 	  rx_full;
   
   wire 	  bypass_hb, want_q;
   wire [4:0] 	  bitwidth;
   wire [3:0] 	  bitshift;
   
   setting_reg #(`FR_RX_FORMAT) sr_rxformat(.clock(rxclk),.reset(reset_regs),
					    .strobe(serial_strobe),.addr(serial_addr),.in(serial_data),
					    .out({bypass_hb,want_q,bitwidth,bitshift}));

   // USB Read Side of FIFO
   always @(negedge usbclk)
     have_pkt_rdy <= (rxfifolevel >= 256);

   // 257 Bug Fix
   reg [8:0] 	  read_count;
   always @(negedge usbclk)
     if(bus_reset)
       read_count <= 0;
     else if(RD)
       read_count <= read_count + 1;
     else
       read_count <= 0;
   
   // FIFO
   wire 	  ch0_in, ch0_out, iq_out;
   assign 	  ch0_in = (phase == 1);

   fifo_4k_18 rxfifo 
     ( // DSP Write Side
       .data ( {ch0_in, phase[0], fifodata} ),
       .wrreq (~rx_full & (phase != 0)),
       .wrclk ( rxclk ),
       .wrfull ( rx_full ),
       .wrempty ( ),
       .wrusedw ( ),
       // USB Read Side
       .q ( {ch0_out,iq_out,usbdata} ),
       .rdreq ( RD & ~read_count[8] ), 
       .rdclk ( ~usbclk ),
       .rdfull ( ),
       .rdempty ( ),
       .rdusedw ( rxfifolevel ),
       // Async, shared
       .aclr ( reset ) );

   // DSP Write Side of FIFO
   reg [15:0] ch_0_reg;
   reg [15:0] ch_1_reg;
   reg [15:0] ch_2_reg;
   reg [15:0] ch_3_reg;
   reg [15:0] ch_4_reg;
   reg [15:0] ch_5_reg;
   reg [15:0] ch_6_reg;
   reg [15:0] ch_7_reg;

   always @(posedge rxclk)
     if (rxstrobe)
       begin
         ch_0_reg <= ch_0;
         ch_1_reg <= ch_1;
         ch_2_reg <= ch_2;
         ch_3_reg <= ch_3;
         ch_4_reg <= ch_4;
         ch_5_reg <= ch_5;
         ch_6_reg <= ch_6;
         ch_7_reg <= ch_7;
       end

   reg [3:0] phase;
   always @(posedge rxclk)
     if(reset)
       phase <= 4'd0;
     else if(phase == 0)
       begin
	  if(rxstrobe)
	    phase <= 4'd1;
       end
     else if(~rx_full)
       if(phase == ((bitwidth == 5'd8) ? (channels>>1) : channels))
	 phase <= 4'd0;
       else
	 phase <= phase + 4'd1;
   
   assign    fifodata = (bitwidth == 5'd8) ? fifodata_8 : fifodata_16;
   
   assign    fifodata_8 = {round_8(top),round_8(bottom)};
   reg [15:0] top,bottom;
   
   function [7:0] round_8;
      input [15:0] in_val;
      
      round_8 = in_val[15:8] + (in_val[15] & |in_val[7:0]);
   endfunction // round_8
      
   always @*
     case(phase)
       4'd1 : begin
	  bottom = ch_0_reg;
	  top = ch_1_reg;
       end
       4'd2 : begin
	  bottom = ch_2_reg;
	  top = ch_3_reg;
       end
       4'd3 : begin
	  bottom = ch_4_reg;
	  top = ch_5_reg;
       end
       4'd4 : begin
	  bottom = ch_6_reg;
	  top = ch_7_reg;
       end
       default : begin
	  top = 16'hFFFF;
	  bottom = 16'hFFFF;
       end
     endcase // case(phase)
   
   always @*
     case(phase)
       4'd1 : fifodata_16 = ch_0_reg;
       4'd2 : fifodata_16 = ch_1_reg;
       4'd3 : fifodata_16 = ch_2_reg;
       4'd4 : fifodata_16 = ch_3_reg;
       4'd5 : fifodata_16 = ch_4_reg;
       4'd6 : fifodata_16 = ch_5_reg;
       4'd7 : fifodata_16 = ch_6_reg;
       4'd8 : fifodata_16 = ch_7_reg;
       default : fifodata_16 = 16'hFFFF;
     endcase // case(phase)
   
   // Detect overrun
   reg clear_status_dsp, rx_overrun_dsp;
   always @(posedge rxclk)
     clear_status_dsp <= clear_status;

   always @(negedge usbclk)
     rx_overrun <= rx_overrun_dsp;

   always @(posedge rxclk)
     if(reset)
       rx_overrun_dsp <= 1'b0;
     else if(rxstrobe & (phase != 0))
       rx_overrun_dsp <= 1'b1;
     else if(clear_status_dsp)
       rx_overrun_dsp <= 1'b0;

   // Debug bus
   //
   // 15:0  rxclk  domain => TXA 15:0
   // 31:16 usbclk domain => RXA 15:0
   
   assign debugbus[0]     = reset;
   assign debugbus[1]     = reset_regs;
   assign debugbus[2]     = rxstrobe;
   assign debugbus[6:3]   = channels;
   assign debugbus[7]     = rx_full;
   assign debugbus[11:8]  = phase;
   assign debugbus[12]    = ch0_in;
   assign debugbus[13]    = clear_status_dsp;
   assign debugbus[14]    = rx_overrun_dsp;
   assign debugbus[15]    = rxclk;

   assign debugbus[16]    = bus_reset;   
   assign debugbus[17]    = RD;
   assign debugbus[18]    = have_pkt_rdy;
   assign debugbus[19]    = rx_overrun;
   assign debugbus[20]    = read_count[0];
   assign debugbus[21]    = read_count[8];
   assign debugbus[22]    = ch0_out;
   assign debugbus[23]    = iq_out;
   assign debugbus[24]    = clear_status;
   assign debugbus[30:25] = 0;   
   assign debugbus[31]    = usbclk;
   
endmodule // rx_buffer