// usb_interface.v

//-----------------------------------------------------------------------
//-
//- Entity name : usb_interface
//-
//-----------------------------------------------------------------------


module usb_full_interface_v(

    n_reset, 
    clk, 
    RXF,
    RD,
    TXE,
    WR,
    USB_DATA,
    single_data_bus,
    subadd,
    data,
    n_write,
    n_read,
    n_sync,
    n_wait,
    interrupt,
    read_req,
    busy);

    input n_reset;
    input clk;
    input RXF;
    output RD;
    input TXE;
    output WR;
    inout [7:0] USB_DATA;
    input single_data_bus;
    output [6:0] subadd;
    inout [7:0] data;
    output n_write;
    output n_read;
    output n_sync;
    input n_wait;
    input interrupt;
    input read_req;
    output busy;

   

  // sorties
   
   wire RD;
   wire [7:0] USB_DATA;
   wire WR;
   reg [6:0] subadd;
   wire [7:0] data;
   wire n_write;
   wire n_read;
   wire n_sync;
   wire busy;


  // signaux internes

    reg rxf_r1, rxf_r2;
    reg txe_r1, txe_r2;
    reg interrupt_r1, interrupt_r2;
    reg read_req_r1, read_req_r2;
    reg [7:0]usb_data_in;
    reg [7:0] usb_data_out;
    reg  [7:0]data_in;
    reg [7:0] data_out;
    reg n_write_r;
    wire [6:0]subaddfromrx;
    wire n_syncfromrx;
	wire n_writefromrx;
    wire busyfromrx;
    wire interrupt_torx;
    wire interrupt_totx;
    wire interrupt_toreadreq;
    wire header_error;
    wire header_ok;
    wire trailer_error;
    wire busyrx;
    wire busytx;
    wire busyreadreq;
    wire endtx;
    wire [7:0] NdataLsbfromrx;
    wire [7:0] NdataMsbfromrx;
    wire runtxfromrx;
    wire [7:0]datafromrx;
    reg [15:0] Ndata;
    wire [7:0] usb_data_fromtx;
    wire n_syncfromtx;
    wire n_readfromtx;
    wire busyfromtx;
    wire runtx;
    wire [6:0]subaddtotx;
    wire [7:0] status_byte;
    wire status_byte_ok;
    wire endinterrupt;
    wire n_readfromreadreq;
    wire [7:0] NdataLsbfromreadreq;
    wire runtxfromreadreq;
    wire n_readfromint;
    wire n_syncfromint;
    wire header_interrupt;
    wire trailer_interrupt;
    wire user_interrupt;
    wire busyrx_toreadreq;
    wire busytx_toreadreq;
    wire interrupt_latch;
    wire rxf_latch;
    reg WR_r;

//-----------------------------------------------------------------------//
//-                       usb_data et data et subadd                    -// 
//-----------------------------------------------------------------------//

always @(posedge clk or negedge n_reset)
begin
 if( n_reset == 1'b0)
 begin 
 n_write_r <= 1'b1;
  WR_r <= 1'b0;
 end
else 
begin
  n_write_r <= n_write;
  WR_r <= WR;
 end
end

//INPUT 

always @(single_data_bus or data or USB_DATA)
 begin
  usb_data_in <= USB_DATA;
  if(single_data_bus == 1'b0)
   data_in <= data;
  else
   data_in <= USB_DATA;
end

// OUTPUT

always @(single_data_bus or n_write or n_write_r or WR or WR_r or datafromrx or usb_data_fromtx)
 begin
  if(single_data_bus == 1'b0)
   begin
    if (n_write == 1'b0 || n_write_r == 1'b0)
     data_out     <= datafromrx;
   else data_out <= 8'bZZZZZZZZ;

    if(WR == 1'b1  || WR_r == 1'b1) 
     usb_data_out <= usb_data_fromtx;
    else usb_data_out <= 8'bZZZZZZZZ;

   end
  else
   begin
    data_out <= 8'bZZZZZZZZ;
    if (n_write == 1'b0 || n_write_r == 1'b0)
    usb_data_out <= datafromrx;
    else if (WR == 1'b1 || WR_r == 1'b1)
    usb_data_out <= usb_data_fromtx;
    else
    usb_data_out <= 8'bZZZZZZZZ;
   end
end

assign USB_DATA = usb_data_out;
assign data = data_out;

always @(busyreadreq or subaddfromrx or user_interrupt)
begin
 if ( busyreadreq == 1'b1 || user_interrupt == 1'b1)
  subadd <= 7'b1111111;
 else 
  subadd <= subaddfromrx;
 end
 

//-----------------------------------------------------------------------//
//-                      n_read, n_write, n_sync, busy                  -// 
//-----------------------------------------------------------------------//

assign n_read = n_readfromtx & n_readfromreadreq & n_readfromint;
assign n_sync = n_syncfromrx & n_syncfromtx & n_syncfromint;
assign n_write = n_writefromrx;
assign busy = busyfromrx | busyfromtx | busyreadreq;

//-----------------------------------------------------------------------//
//-                        RXUNIT INSTANTIATION                         -// 
//-----------------------------------------------------------------------//

rxunit m_rxunit(

    .n_reset(n_reset),
    .clk(clk),
    .rxf(rxf_r2),
    .rd(RD),
    .usb_data(usb_data_in),
    .data(datafromrx),
    .subadd(subaddfromrx),
    .n_write(n_writefromrx),
    .n_sync(n_syncfromrx),
    .n_wait(n_wait),
    .busy(busyfromrx),
    .interrupt(interrupt_torx),
    .header_error(header_error),
    .header_ok(header_ok),
    .trailer_error(trailer_error),
    .busyrx(busyrx),
    .busytx(busytx),
    .readrequestbusy(busyreadreq),
    .rxf_latch(rxf_latch),
    .endtx(endtx),
    .NdataLsb(NdataLsbfromrx),
    .NdataMsb(NdataMsbfromrx),
    .runtx(runtxfromrx)
);


//-----------------------------------------------------------------------//
//-                        signaux pour rxunit                          -// 
//-----------------------------------------------------------------------//

assign interrupt_torx = interrupt_latch;

//-----------------------------------------------------------------------//
//-                       TXUNIT INSTANTIATION                          -// 
//-----------------------------------------------------------------------//

txunit m_txunit (
  .n_reset(n_reset),
  .clk(clk),
  .TXE(txe_r2),
  .WR(WR),
  .usb_data(usb_data_fromtx),
  .n_sync(n_syncfromtx),
  .n_read(n_readfromtx),
  .n_wait(n_wait),
  .data(data_in),
  .busy(busyfromtx),
  .runtx(runtx),
  .Ndata(Ndata),
  .interrupt(interrupt_totx),
  .subadd(subaddtotx),
  .status_byte(status_byte),
  .status_byte_ok(status_byte_ok),
  .busyrx(busyrx),
  .busytx(busytx),
  .endtx(endtx),
  .endinterrupt(endinterrupt)
);

//-----------------------------------------------------------------------//
//-                        signaux pour txunit                          -// 
//-----------------------------------------------------------------------//

assign interrupt_totx = interrupt_latch;

always @ (runtxfromrx or runtxfromreadreq or NdataMsbfromrx or NdataLsbfromrx 
          or NdataLsbfromreadreq)
begin
 if ( runtxfromrx == 1'b1)
  Ndata <= {NdataMsbfromrx, NdataLsbfromrx};
 else if ( runtxfromreadreq == 1'b1)
  Ndata <= {8'h00,NdataLsbfromreadreq};
 else
  Ndata <= 16'h0000;
end


assign subaddtotx = subadd;

assign runtx = runtxfromreadreq | runtxfromrx;
//-----------------------------------------------------------------------//
//-                       READREQ INSTANTIATION                          -// 
//-----------------------------------------------------------------------//

readrequnit m_readrequnit(
    .n_reset(n_reset),    
    .clk(clk),
    .read_req(read_req_r2),
    .n_read(n_readfromreadreq),
    .data(data_in),
    .n_wait(n_wait),
    .interrupt(interrupt_toreadreq),
    .txbusy(busytx_toreadreq),
    .NdataLsb(NdataLsbfromreadreq),
    .runtx(runtxfromreadreq),
    .endtx(endtx),
    .rxbusy(busyrx_toreadreq),
    .busyreadreq(busyreadreq)
);

assign interrupt_toreadreq = interrupt_latch; 
assign busyrx_toreadreq = busyrx | rxf_latch;
assign busytx_toreadreq = busytx | runtxfromrx;

//-----------------------------------------------------------------------//
//-                       INTERRUPTUNIT INSTANTIATION                    -// 
//-----------------------------------------------------------------------//

interruptunit m_interruptunit(
      .n_reset(n_reset),
      .clk(clk),
      .interrupt(interrupt_r2),
      .n_read(n_readfromint),
      .n_sync(n_syncfromint),
      .n_wait(n_wait),
      .data(data_in),
      .header_error(header_error),
      .header_ok(header_ok),
      .trailer_error(trailer_error),
      .rxbusy(busyrx),
      .txbusy(busytx),
      .header_interrupt(header_interrupt),
      .trailer_interrupt(trailer_interrupt),
      .status_byte(status_byte),
      .status_byte_ok(status_byte_ok),
      .interrupt_ok(endinterrupt),
      .interrupt_latch_out(interrupt_latch),
      .user_interrupt(user_interrupt)
);


//-----------------------------------------------------------------------//
//-                        signaux pour interruptunit                   -// 
//-----------------------------------------------------------------------//

//-----------------------------------------------------------------------//
//-                        synchronisations                             -// 
//-----------------------------------------------------------------------//



always @ ( posedge clk or negedge n_reset)
begin
 if ( n_reset == 1'b0)
  begin
   rxf_r1 <= 1'b1;
   rxf_r2 <= 1'b1;
   txe_r1 <= 1'b0;
   txe_r2 <= 1'b0;
   interrupt_r1 <= 1'b1;
   interrupt_r2 <= 1'b1;
   read_req_r1 <= 1'b1;
   read_req_r2 <= 1'b1;
 end
else 
 begin
   rxf_r1 <= RXF;
   rxf_r2 <= rxf_r1;
   txe_r1 <= TXE;
   txe_r2 <= txe_r1;
   interrupt_r1 <= interrupt;
   interrupt_r2 <= interrupt_r1;
   read_req_r1 <= read_req;
   read_req_r2 <= read_req_r1;
 end
end

//-----------------------------------------------------------------------//
//-                       FIN                                           -// 
//-----------------------------------------------------------------------//


endmodule
