// -*- 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.  Each fifo line is 4 bytes
// Fifo has 1024 or 2048 lines

module bus_interface
  (     input usbclk,
	input reset,
	inout [15:0] usbdata,   // TRISTATE
	input wire [5:0] usbctl,
	output wire [5:0] usbrdy,
	output [31:0] txdata,
	input [31:0] rxdata,
	input txclk,
	input txstrobe,
	input rxclk,
	input rxstrobe,
	output [11:0] debugbus,
	input clear_status
	);

   parameter   IN_CHANNELS = 1;
   parameter   OUT_CHANNELS = 1;
   parameter   bitmask = (IN_CHANNELS*2)-1;

   wire have_space, have_pkt_rdy;
   wire WR, RD, OE;
   reg tx_underrun, rx_overrun;
   
   assign WR = usbctl[0];
   assign RD = usbctl[1];
   assign OE = usbctl[2];
   
   assign usbrdy[0] = have_space;
   assign usbrdy[1] = have_pkt_rdy;
   assign usbrdy[2] = tx_underrun;
   assign usbrdy[3] = rx_overrun;
   
   reg [IN_CHANNELS*2*16-1:0] fifo_in;
   wire [OUT_CHANNELS*2*16-1:0] fifo_out;
   
   wire [15:0] usbdata_in = usbdata;
   
   reg select_out;
   reg select_in;
   
   reg commit;
   reg rd_next;
   reg [15:0] usbdata_out;
   wire [10:0] txfifolevel,rxfifolevel;
   reg [8:0] write_count;
   wire tx_empty;
   wire tx_full;
   wire rx_empty;
   wire rx_full;
   wire [31:0] txd;
   wire rdreq;
	
   // Tri-state bus macro
   bustri bustri(.data(usbdata_out),
		 .enabledt(OE),
		 .tridata(usbdata)  );

   //////////////////////////////////////////////
   // TX Side (USB --> DAC)
   always @(posedge usbclk, posedge reset)
     begin
	if(reset)
	  begin
	     fifo_in <= #1 0;
	     write_count <= #1 0;
	  end
	else 
	  if(WR & ~write_count[8])
	    begin
	       case(write_count[0])
		 1'b0 : fifo_in[31:16] <= #1 usbdata_in;  // I
		 1'b1 : fifo_in[15:0] <= #1 usbdata_in;   // Q
	       endcase
	       write_count <= #1 write_count + 9'd1;
	    end
	  else
	    write_count <= #1 WR ? write_count : 9'b0;
     end
   
   always @(posedge usbclk)
     if(reset)
       commit <= #1 1'b0;
     else
       if(write_count[0] && ~write_count[8] && WR)
	 commit <= #1 1'b1;
       else 
	 commit <= #1 1'b0;
   
   assign rdreq = txstrobe & !tx_empty;
   assign txdata = tx_empty ? 32'b0 : txd;
   
   always @(posedge txclk)
     if(reset)
       tx_underrun <= 1'b0;
     else if(txstrobe & tx_empty)
       tx_underrun <= 1'b1;
     else if(clear_status)
       tx_underrun <= 1'b0;

   fifo_1c_2k	txfifo (.data ( fifo_in ),
			.wrreq ( commit ),
			.wrclk ( usbclk ),

			.q ( txd ),			
			.rdreq ( rdreq),
			.rdclk ( txclk ),
			
			.aclr ( reset ),

			.rdempty ( tx_empty ),
			.rdusedw (  ),
			.wrfull ( tx_full ),
			.wrusedw ( txfifolevel )
			);
   
   assign have_space = (txfifolevel <= (2048-128));
   
   //////////////////////////////
   // Receive FIFO (ADC --> USB)

   always @(posedge rxclk)
     if(reset)
       rx_overrun <= 1'b0;
     else if(rxstrobe & rx_full)
       rx_overrun <= 1'b1;
     else if(clear_status)
       rx_overrun <= 1'b0;

   always @(select_out, fifo_out)
     case(select_out)
       0 : usbdata_out = fifo_out[31:16];  // I
       1 : usbdata_out = fifo_out[15:0];   // Q
     endcase
  
/*
	always @(posedge usbclk, posedge reset)
	if(reset)
		usbdata_out <= #1 16'b0;
	else
		if(select_out)
			usbdata_out = fifo_out[31:16];
		else
			usbdata_out = fifo_out[15:0];
	*/
	 
   always @(negedge usbclk, posedge reset)
     if(reset)
       select_out <= #1 1'b0;
     else if(~RD)
       select_out <= #1 1'b0;
     else 
       select_out <= #1 ~select_out;
 
   fifo_1c_2k	rxfifo (.data ( rxdata ), // counter ),
			.wrreq (rxstrobe & ~rx_full ),
			.wrclk ( rxclk ),
			
			.q ( fifo_out ),
			.rdreq ( select_out ),// & RD ), // FIXME
			.rdclk ( usbclk ),
			
			.aclr ( reset ),
			
			.rdempty ( rx_empty ),
			.rdusedw ( rxfifolevel ),
			.wrfull ( rx_full ),
			.wrusedw (  )
			);
   
   assign have_pkt_rdy = (rxfifolevel >= 128);

   // Debugging Aids
   assign debugbus[0] = tx_underrun;
   assign debugbus[1] = rx_overrun;
   assign debugbus[2] = tx_empty;
   assign debugbus[3] = tx_full;
   assign debugbus[4] = rx_empty;
   assign debugbus[5] = rx_full;
   assign debugbus[6] = txstrobe;
   assign debugbus[7] = rxstrobe;
   assign debugbus[8] = select_out;
   assign debugbus[9] = rxstrobe & ~rx_full;
   assign debugbus[10] = have_space;
   assign debugbus[11] = have_pkt_rdy;
   
endmodule // bus_interface