// ----------------------------------------------------------------------
// "THE BEER-WARE LICENSE" (Revision 42):
//    <yasu@prosou.nu> wrote this file. As long as you retain this
//    notice you can do whatever you want with this stuff. If we meet
//    some day, and you think this stuff is worth it, you can buy me a
//    beer in return Yasunori Osana at University of the Ryukyus,
//    Japan.
// ----------------------------------------------------------------------
// OpenFC project: an open FPGA accelerated cluster toolkit
// 
// Modules in this file:
//    top: a top-level module for Xilinx KCU1500 board (with RIFFA DMA)
// ----------------------------------------------------------------------

`default_nettype none

module top
  ( // Clock signals
    input wire        SYSCLK0_300_P, SYSCLK0_300_N,
    
    // PCIe signals
    input wire        PCIE_REFCLK_P, PCIE_REFCLK_N,
    input wire        PCIE_RESET_N, 
    input wire [3:0]  PCIE_RXP, PCIE_RXN,
    output wire [3:0] PCIE_TXP, PCIE_TXN,

    // QSFP signals
    input wire [3:0]  QSFP0_RXN, QSFP0_RXP,
    output wire [3:0] QSFP0_TXN, QSFP0_TXP,
    input wire        QSFP0_REFCLKN, QSFP0_REFCLKP,

`ifdef ENABLE_QSFP1    
    input wire [3:0]  QSFP1_RXN, QSFP1_RXP,
    output wire [3:0] QSFP1_TXN, QSFP1_TXP,
    input wire        QSFP1_REFCLKN, QSFP1_REFCLKP,
`endif
    
    // State LEDs
    output wire [7:0] LED
    );

   // ------------------------------------------------------------
   // Clock managers

   wire               CLK100;
   wire               DCM_LOCKED;

   kcu1500_clk cm
     ( .CLK300P(SYSCLK0_300_P), .CLK300N(SYSCLK0_300_N),
       .CLK100 (CLK100),
       .DCM_LOCKED(DCM_LOCKED) );
     

   // ------------------------------------------------------------
   // PCIe interface (2 router ports)

   wire               CLK, RST;
   
   wire [1:0]         PCI_D_BP,    PCI_Q_BP;
   wire [1:0]         PCI_D_VALID, PCI_Q_VALID;
   wire [127:0]       PCI_D,       PCI_Q;
   
   pcie_port pci
     ( .PCIE_RESET_N(PCIE_RESET_N),
       .PCIE_REFCLK_P(PCIE_REFCLK_P),
       .PCIE_REFCLK_N(PCIE_REFCLK_N),

       .PCIE_RXP(PCIE_RXP), .PCIE_RXN(PCIE_RXN),
       .PCIE_TXP(PCIE_TXP), .PCIE_TXN(PCIE_TXN),

       .CLK_OUT(CLK), .RST_OUT(RST),
       
       .D(PCI_D), .D_BP(PCI_D_BP), .D_VALID(PCI_D_VALID),   // O
       .Q(PCI_Q), .Q_BP(PCI_Q_BP), .Q_VALID(PCI_Q_VALID) ); // I

   // ------------------------------------------------------------
   // Aurora interface

   wire [7:0]         AURORA_PE_RST = 0;
   wire               QSFP0_REFCLK;
   IBUFDS_GTE3 qsfp0_clkb( .I(QSFP0_REFCLKP), .IB(QSFP0_REFCLKN), .CEB(0),
                           .O(QSFP0_REFCLK) );

`ifndef ENABLE_QSFP1   
   wire [255:0]       GT_D, GT_Q;
   wire [3:0]         GT_D_VALID, GT_Q_VALID, GT_D_BP, GT_Q_BP;
   wire [3:0]         GT_UP;
`else
   wire [511:0]       GT_D, GT_Q;
   wire [7:0]         GT_D_VALID, GT_Q_VALID, GT_D_BP, GT_Q_BP;
   wire [7:0]         GT_UP;
`endif
   
   aurora_quad au_qsfp0
     ( .CLK250      (CLK), 
       .SYS_RST     (RST),
       .PE_RST      (AURORA_PE_RST[3:0]),
       .CLK100      (CLK100),
       .GTREFCLK    (QSFP0_REFCLK),
       .DCM_LOCKED  (DCM_LOCKED),
 
       .TXP(QSFP0_TXP), .TXN(QSFP0_TXN),
       .RXP(QSFP0_RXP), .RXN(QSFP0_RXP),

       .D       (GT_D       [255:0]), .Q       (GT_Q       [255:0]),
       .D_VALID (GT_D_VALID [  3:0]), .Q_VALID (GT_Q_VALID [  3:0]),
       .D_BP    (GT_D_BP    [  3:0]), .Q_BP    (GT_Q_BP    [  3:0]),

       .CH_UP(GT_UP[3:0]) );

`ifdef ENABLE_QSFP1   
   wire               QSFP1_REFCLK; 
   IBUFDS_GTE3 qsfp1_clkb( .I(QSFP1_REFCLKP), .IB(QSFP1_REFCLKN), .CEB(0),
                           .O(QSFP1_REFCLK) );

   aurora_quad au_qsfp1
     ( .CLK250      (CLK), 
       .SYS_RST     (RST),
       .PE_RST      (AURORA_PE_RST[7:4]),
       .CLK100      (CLK100),
       .GTREFCLK    (QSFP1_REFCLK),
       .DCM_LOCKED  (DCM_LOCKED),
 
       .TXP(QSFP1_TXP), .TXN(QSFP1_TXN),
       .RXP(QSFP1_RXP), .RXN(QSFP1_RXP),

       .D       (GT_D       [511:256]), .Q       (GT_Q       [511:256]),
       .D_VALID (GT_D_VALID [  7:  4]), .Q_VALID (GT_Q_VALID [  7:  4]),
       .D_BP    (GT_D_BP    [  7:  4]), .Q_BP    (GT_Q_BP    [  7:  4]),

       .CH_UP(GT_UP[7:4]) );
`endif //  `ifdef ENABLE_QSFP1

   // ------------------------------------------------------------
   // ICAP instance

   wire               ICAP_PE_RST;
   wire [63:0]        ICAP_D, ICAP_Q;
   wire               ICAP_D_VALID, ICAP_Q_VALID, ICAP_D_BP, ICAP_Q_BP;
   wire               ICAP_BUSY;
   
   pe_icap icap
     ( .CLK250(CLK), .CLK100(CLK100), .SYS_RST(RST), .PE_RST(ICAP_PE_RST),
       .D(ICAP_D), .D_VALID(ICAP_D_VALID), .D_BP(ICAP_D_BP),
       .Q(ICAP_Q), .Q_VALID(ICAP_Q_VALID), .Q_BP(ICAP_Q_BP),
       .BUSY(ICAP_BUSY) );

   // ------------------------------------------------------------
   // PE instance

   wire               PE_RST;
   wire [127:0]        PE_D, PE_Q;
   wire [1:0]          PE_D_VALID, PE_Q_VALID, PE_D_BP, PE_Q_BP,
                       PE_Q_VALIDi;
   
   pe pe
     ( .CLK(CLK), .SYS_RST(RST),.PE_RST (PE_RST),
       .D (PE_D[ 63: 0]), .D_VALID (PE_D_VALID [0]), .D_BP (PE_D_BP[0]),
       .Q (PE_Q[ 63: 0]), .Q_VALID (PE_Q_VALIDi[0]), .Q_BP (PE_Q_BP[0]),
       .D2(PE_D[127:64]), .D2_VALID(PE_D_VALID [1]), .D2_BP(PE_D_BP[1]),
       .Q2(PE_Q[127:64]), .Q2_VALID(PE_Q_VALIDi[1]), .Q2_BP(PE_Q_BP[1]) );

   assign PE_Q_VALID = ICAP_BUSY ? 0 : PE_Q_VALIDi;
   
   // ------------------------------------------------------------
   // Router instance

   // Port 10-13 = QSFP1 (if enabled)
   // Port  6- 9 = QSFP0
   // Port  4- 5 = PCIe
   // Port     3 = ICAP
   // Port  1, 2 = PE

   wire  [12:0] ROUTER_Q_SOF;

`ifndef ENABLE_QSFP1
   defparam ro.NumPorts = 09;
`else
   defparam ro.NumPorts = 13;
`endif
   
   defparam ro.PassThrough = 'b000_111; // Note: PE_ICAP requires PT!
   
   router ro
     ( .CLK(CLK), .RST(RST),
       .D      ({GT_Q,       PCI_D,       ICAP_Q,       PE_Q      }), // I
       .D_VALID({GT_Q_VALID, PCI_D_VALID, ICAP_Q_VALID, PE_Q_VALID}), // I
       .D_BP   ({GT_Q_BP,    PCI_D_BP,    ICAP_Q_BP,    PE_Q_BP   }), // O
       
       .Q      ({GT_D,       PCI_Q,       ICAP_D,       PE_D      }), // O
       .Q_VALID({GT_D_VALID, PCI_Q_VALID, ICAP_D_VALID, PE_D_VALID}), // O
       .Q_BP   ({GT_D_BP,    PCI_Q_BP,    ICAP_D_BP,    PE_D_BP   }), // I
       .Q_SOF  (ROUTER_Q_SOF) ); // O: to generate PE_RST

   assign PE_RST      = ROUTER_Q_SOF[0];
   assign ICAP_PE_RST = ROUTER_Q_SOF[2];

   // ------------------------------------------------------------
   // State LEDs

   // 0: PCIe Clock    4: 
   // 1: PCIe 0        5:
   // 2: PCIe 1        6: 
   // 3:               7: 

   wire [1:0]   QSFP_LINK, QSFP_ACT;

`ifdef ENABLE_QSFP1
   assign QSFP_LINK[1] =  &GT_UP     [7:4];
   assign QSFP_ACT [1] = |(GT_D_VALID[7:4] | GT_Q_VALID[7:4]);
`else
   assign QSFP_LINK[1] = 0;
   assign QSFP_ACT [1] = 0;
`endif

   assign QSFP_LINK[0] =  &GT_UP     [3:0];
   assign QSFP_ACT [0] = |(GT_D_VALID[3:0] | GT_Q_VALID[3:0]);
   
   wire [7:0] LED_LINK = { QSFP_LINK,   // [1:0]
                           3'b0,        // Not connected
                           3'b111 };    // PCIe is always up

   wire [7:0] LED_ACT  = { QSFP_ACT,    // [1:0]
                           3'b0,        // Not connected
                           PCI_D_VALID[1] | PCI_Q_VALID[1],
                           PCI_D_VALID[0] | PCI_Q_VALID[0],
                           1'b1 };  // PCIe clock 

   generate
      genvar  i;
      for (i=0; i<8; i=i+1) begin : led_gen
         link_act led ( .CLK(CLK), .RST(RST), .LED(LED[i]), 
                        .LINK(LED_LINK[i]),   .ACT(LED_ACT[i])); 
      end
   endgenerate
   
endmodule // top

`default_nettype wire