////////////////////////////////////////////////////////////////////////////////
//
// Filename: ddr3_phy.v
// Project: UberDDR3 - An Open Source DDR3 Controller
//
// Purpose: PHY component for the DDR3 controller. Handles the primitives such
// as IOSERDES, IODELAY, and IOBUF. These generates the signals connected to 
// the DDR3 RAM.
//
// Engineer: Angelo C. Jacobo
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2023-2025  Angelo Jacobo
// 
//     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 3 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, see <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////

`default_nettype none
`timescale 1ps / 1ps
//`define DEBUG_DQS // uncomment to route the raw DQS to output port for debugging

module ddr3_phy #(
    parameter     CONTROLLER_CLK_PERIOD = 10_000, //ps, clock period of the controller interface
                  DDR3_CLK_PERIOD = 2_500, //ps, clock period of the DDR3 RAM device (must be 1/4 of the CONTROLLER_CLK_PERIOD) 
                  ROW_BITS = 14,   //width of row address
                  BA_BITS = 3,
                  DQ_BITS = 8,
                  LANES = 8,
                  DUAL_RANK_DIMM = 0, // enable dual rank DIMM (1 =  enable, 0 = disable)
    parameter[0:0] ODELAY_SUPPORTED = 1, //set to 1 when ODELAYE2 is supported
                   USE_IO_TERMINATION = 0, //use IOBUF_DCIEN and IOBUFDS_DCIEN when 1
                   NO_IOSERDES_LOOPBACK = 1, // don't use IOSERDES loopback for bitslip training
              // The next parameters act more like a localparam (since user does not have to set this manually) but was added here to simplify port declaration
    parameter serdes_ratio = 4, // this controller is fixed as a 4:1 memory controller (CONTROLLER_CLK_PERIOD/DDR3_CLK_PERIOD = 4)
              wb_data_bits = DQ_BITS*LANES*serdes_ratio*2,
              wb_sel_bits = wb_data_bits / 8,
              //4 is the width of a single ddr3 command {cs_n, ras_n, cas_n, we_n} plus 3 (ck_en, odt, reset_n) plus bank bits plus row bits
              cmd_len = 4 + 3 + BA_BITS + ROW_BITS + 2*DUAL_RANK_DIMM
    )(
        input wire i_controller_clk, i_ddr3_clk, i_ref_clk,
        input wire i_ddr3_clk_90, //required only when ODELAY_SUPPORTED is zero
        input wire i_rst_n,
        // Controller Interface
        input wire i_controller_reset,
        input wire[cmd_len*serdes_ratio-1:0] i_controller_cmd,
        input wire i_controller_dqs_tri_control, i_controller_dq_tri_control,
        input wire i_controller_toggle_dqs,
        input wire[wb_data_bits-1:0] i_controller_data,
        input wire[wb_sel_bits-1:0] i_controller_dm,
        input wire[4:0] i_controller_odelay_data_cntvaluein,i_controller_odelay_dqs_cntvaluein,
        input wire[4:0] i_controller_idelay_data_cntvaluein,i_controller_idelay_dqs_cntvaluein,
        input wire[LANES-1:0] i_controller_odelay_data_ld, i_controller_odelay_dqs_ld,
        input wire[LANES-1:0] i_controller_idelay_data_ld, i_controller_idelay_dqs_ld,
        input wire[LANES-1:0] i_controller_bitslip,
        input wire i_controller_write_leveling_calib,
        output wire[DQ_BITS*LANES*8-1:0] o_controller_iserdes_data,
        output wire[LANES*8-1:0] o_controller_iserdes_dqs,
        output wire[LANES*8-1:0] o_controller_iserdes_bitslip_reference,
        output wire o_controller_idelayctrl_rdy,
        // DDR3 I/O Interface
        output wire[DUAL_RANK_DIMM:0] o_ddr3_clk_p,o_ddr3_clk_n,
        output wire o_ddr3_reset_n,
        output wire[DUAL_RANK_DIMM:0] o_ddr3_cke, // CKE
        output wire[DUAL_RANK_DIMM:0] o_ddr3_cs_n, // chip select signal
        output wire o_ddr3_ras_n, // RAS#
        output wire o_ddr3_cas_n, // CAS#
        output wire o_ddr3_we_n, // WE#
        output wire[ROW_BITS-1:0] o_ddr3_addr,
        output wire[BA_BITS-1:0] o_ddr3_ba_addr,
        inout wire[(DQ_BITS*LANES)-1:0] io_ddr3_dq,
        inout wire[(DQ_BITS*LANES)/8-1:0] io_ddr3_dqs, io_ddr3_dqs_n,
        output wire[LANES-1:0] o_ddr3_dm,
        output wire[DUAL_RANK_DIMM:0] o_ddr3_odt, // on-die termination
        // DEBUG PHY
        output wire[(DQ_BITS*LANES)/8-1:0] o_ddr3_debug_read_dqs_p,
        output wire[(DQ_BITS*LANES)/8-1:0] o_ddr3_debug_read_dqs_n
    );

    // cmd bit assignment
    localparam CMD_CS_N_2 = cmd_len - 1,
                CMD_CS_N =  DUAL_RANK_DIMM[0]? cmd_len - 2 : cmd_len - 1,
                CMD_RAS_N = DUAL_RANK_DIMM[0]? cmd_len - 3 : cmd_len - 2,
                CMD_CAS_N = DUAL_RANK_DIMM[0]? cmd_len - 4 : cmd_len - 3,
                CMD_WE_N =  DUAL_RANK_DIMM[0]? cmd_len - 5 : cmd_len - 4,
                CMD_ODT =   DUAL_RANK_DIMM[0]? cmd_len - 6 : cmd_len - 5,
                CMD_CKE_2 = DUAL_RANK_DIMM[0]? cmd_len - 7 : cmd_len - 6,
                CMD_CKE =   DUAL_RANK_DIMM[0]? cmd_len - 8 : cmd_len - 6,
                CMD_RESET_N = DUAL_RANK_DIMM[0]? cmd_len - 9 : cmd_len - 7,
                CMD_BANK_START = BA_BITS + ROW_BITS - 1,
                CMD_ADDRESS_START = ROW_BITS - 1;
    localparam SYNC_RESET_DELAY = $rtoi($ceil(52_000/CONTROLLER_CLK_PERIOD)); //52_000 ps of reset pulse width required for IDELAYCTRL 
    //cmd needs to be center-aligned to the positive edge of the 
    //ddr3_clk. This means cmd needs to be delayed by half the ddr3
    //clk period. Subtract by 600ps to include the IODELAY insertion
    //delay. Divide by a delay resolution of 78.125ps per tap to get 
    //the needed tap value.
    localparam CMD_ODELAY_TAP = ((DDR3_CLK_PERIOD/2) - 600)/78.125;

    // Data does not have to be delayed (DQS is the on that has to be
    // delayed and center-aligned to the center eye of data)
    localparam DATA_ODELAY_TAP = 0; 

    //DQS needs to be edge-aligned to the center eye of the data. 
    //This means DQS needs to be delayed by a quarter of the ddr3
    //clk period relative to the data. Subtract by 600ps to include
    //the IODELAY insertion delay. Divide by a delay resolution of 
    //78.125ps per tap to get the needed tap value. Then add the tap
    //value used in data to have the delay relative to the data.
    localparam DQS_ODELAY_TAP = ((DDR3_CLK_PERIOD/4))/78.125 + DATA_ODELAY_TAP;
    
    //Incoming DQS should be 90 degree delayed relative to incoming data
    localparam DATA_IDELAY_TAP = 0; //600ps delay
    localparam DQS_IDELAY_TAP = ((DDR3_CLK_PERIOD/4))/78.125 + DATA_IDELAY_TAP;
    
    genvar gen_index;
    wire[cmd_len-1:0] oserdes_cmd, //serialized(4:1) i_controller_cmd_slot_x 
                      cmd;//delayed oserdes_cmd
    wire[(DQ_BITS*LANES)-1:0] oserdes_data, odelay_data, idelay_data, read_dq;
    wire[LANES-1:0] oserdes_dm, odelay_dm;
    wire[LANES-1:0] odelay_dqs, read_dqs, idelay_dqs;
    wire[DQ_BITS*LANES-1:0] oserdes_dq_tri_control;
    wire[LANES-1:0] oserdes_dqs;
    wire[LANES-1:0] oserdes_dqs_tri_control;
    wire[LANES-1:0] oserdes_bitslip_reference;
    reg[$clog2(SYNC_RESET_DELAY):0] delay_before_release_reset;
    reg sync_rst = 0;
    wire ddr3_clk;
    reg toggle_dqs_q; //past value of i_controller_toggle_dqs
    wire ddr3_clk_delayed;
    wire idelayctrl_rdy;
    wire dci_locked;
    reg[LANES*8-1:0] o_controller_iserdes_bitslip_reference_reg;
    reg[LANES - 1 : 0] shift_bitslip_index;
    
    // initial value of bitslip reference
    initial begin
        o_controller_iserdes_bitslip_reference_reg = {LANES{8'b0001_1110}};
        shift_bitslip_index = 0;
    end
                
    assign o_controller_idelayctrl_rdy = idelayctrl_rdy && dci_locked;
    
`ifdef DEBUG_DQS
    assign o_ddr3_debug_read_dqs_p = io_ddr3_dqs;
    assign o_ddr3_debug_read_dqs_n = io_ddr3_dqs_n;
`else
    assign o_ddr3_debug_read_dqs_p = 0;
    assign o_ddr3_debug_read_dqs_n = 0;
`endif

    //synchronous reset
    always @(posedge i_controller_clk) begin
        if(!i_rst_n || i_controller_reset) begin
            sync_rst <= 1'b1;
            delay_before_release_reset <= SYNC_RESET_DELAY[$clog2(SYNC_RESET_DELAY):0];
            toggle_dqs_q <= 0;
        end
        else begin
            delay_before_release_reset <= (delay_before_release_reset == 0)? 0: delay_before_release_reset - 1;
            sync_rst <= !(delay_before_release_reset == 0);
            toggle_dqs_q <= i_controller_toggle_dqs;
        end
    end

    //PHY cmd 
    generate
        for(gen_index = 0; gen_index < cmd_len; gen_index = gen_index + 1) begin
            // OSERDESE2: Output SERial/DESerializer with bitslip
            //7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                OSERDESE2 #(
            `else 
                OSERDESE2_model #(
            `endif
                .DATA_RATE_OQ("SDR"), // DDR, SDR
                .DATA_RATE_TQ("SDR"), // DDR, SDR
                .DATA_WIDTH(4), // Parallel data width (2-8,10,14)
                .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
                .TRISTATE_WIDTH(1)
            )
            OSERDESE2_cmd(
                .OFB(), // 1-bit output: Feedback path for data
                .OQ(oserdes_cmd[gen_index]), // 1-bit output: Data path output
                .CLK(i_ddr3_clk), // 1-bit input: High speed clock
                .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                .D1(i_controller_cmd[cmd_len*0 + gen_index]),
                .D2(i_controller_cmd[cmd_len*1 + gen_index]),
                .D3(i_controller_cmd[cmd_len*2 + gen_index]),
                .D4(i_controller_cmd[cmd_len*3 + gen_index]),
                .OCE(1'b1), // 1-bit input: Output data clock enable
                .RST(sync_rst), // 1-bit input: Reset
                // unused signals but were added here to make vivado happy
                .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                .SHIFTOUT2(),
                .TBYTEOUT(), // 1-bit output: Byte group tristate
                .TFB(), // 1-bit output: 3-state control
                .TQ(), // 1-bit output: 3-state control
                .D5(),
                .D6(),
                .D7(),
                .D8(),
                // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                .SHIFTIN1(0),
                .SHIFTIN2(0),
                // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                .T1(0),
                .T2(0),
                .T3(0),
                .T4(0),
                .TBYTEIN(0),
                // 1-bit input: Byte group tristate
                .TCE(0)
                // 1-bit input: 3-state clock enable
            );
            // End of OSERDESE2_inst instantiation
      
        end

        if(DUAL_RANK_DIMM) begin // if dual rank enabled, odt_2 and odt_1 will be generated separately
            // OSERDESE2: Output SERial/DESerializer with bitslip
            //7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                OSERDESE2 #(
            `else 
                OSERDESE2_model #(
            `endif
                .DATA_RATE_OQ("SDR"), // DDR, SDR
                .DATA_RATE_TQ("SDR"), // DDR, SDR
                .DATA_WIDTH(4), // Parallel data width (2-8,10,14)
                .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
                .TRISTATE_WIDTH(1)
            )
            OSERDESE2_cmd(
                .OFB(), // 1-bit output: Feedback path for data
                .OQ(o_ddr3_odt[1]), // 1-bit output: Data path output
                .CLK(i_ddr3_clk), // 1-bit input: High speed clock
                .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                .D1(i_controller_cmd[cmd_len*0 + CMD_ODT]),
                .D2(i_controller_cmd[cmd_len*1 + CMD_ODT]),
                .D3(i_controller_cmd[cmd_len*2 + CMD_ODT]),
                .D4(i_controller_cmd[cmd_len*3 + CMD_ODT]),
                .OCE(1'b1), // 1-bit input: Output data clock enable
                .RST(sync_rst), // 1-bit input: Reset
                // unused signals but were added here to make vivado happy
                .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                .SHIFTOUT2(),
                .TBYTEOUT(), // 1-bit output: Byte group tristate
                .TFB(), // 1-bit output: 3-state control
                .TQ(), // 1-bit output: 3-state control
                .D5(),
                .D6(),
                .D7(),
                .D8(),
                // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                .SHIFTIN1(0),
                .SHIFTIN2(0),
                // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                .T1(0),
                .T2(0),
                .T3(0),
                .T4(0),
                .TBYTEIN(0),
                // 1-bit input: Byte group tristate
                .TCE(0)
                // 1-bit input: 3-state clock enable
            );
            // End of OSERDESE2_inst instantiation
        end
    endgenerate 

    // cs[1] when DUAL_RANK_DIMM enabled
    generate    
        if(DUAL_RANK_DIMM) begin
            assign o_ddr3_cs_n[1] = oserdes_cmd[CMD_CS_N_2];
            assign o_ddr3_cs_n[0] = oserdes_cmd[CMD_CS_N];
            assign o_ddr3_cke[1] = oserdes_cmd[CMD_CKE_2];
            assign o_ddr3_cke[0] = oserdes_cmd[CMD_CKE];
            assign o_ddr3_odt[0] = oserdes_cmd[CMD_ODT];
            // o_ddr3_odt[1] will be generated directly by a separate OSERDES
            // if odt[1] and odt[0] uses same output from oserdes, one of them will be unroutable
        end
        else begin
            assign o_ddr3_cs_n = oserdes_cmd[CMD_CS_N];
            assign o_ddr3_cke = oserdes_cmd[CMD_CKE];
            assign o_ddr3_odt = oserdes_cmd[CMD_ODT];
        end
    endgenerate
    assign o_ddr3_ras_n = oserdes_cmd[CMD_RAS_N],
           o_ddr3_cas_n = oserdes_cmd[CMD_CAS_N],
           o_ddr3_we_n = oserdes_cmd[CMD_WE_N],
           o_ddr3_reset_n = oserdes_cmd[CMD_RESET_N],
           o_ddr3_ba_addr = oserdes_cmd[CMD_BANK_START:CMD_ADDRESS_START+1],
           o_ddr3_addr = oserdes_cmd[CMD_ADDRESS_START:0];
    
    if(ODELAY_SUPPORTED) begin
        // OSERDESE2: Output SERial/DESerializer with bitslip
        //7 Series
        // Xilinx HDL Libraries Guide, version 13.4
        `ifndef SIM_MODEL
            OSERDESE2 #(
        `else 
            OSERDESE2_model #(
        `endif
            .DATA_RATE_OQ("DDR"), // DDR, SDR
            .DATA_RATE_TQ("SDR"), // DDR, SDR
            .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
            .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
            .TRISTATE_WIDTH(1)
        )
        OSERDESE2_clk(
            .OFB(ddr3_clk), // 1-bit output: Feedback path for data
            .OQ(), // 1-bit output: Data path output
            .CLK(i_ddr3_clk), // 1-bit input: High speed clock
            .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
            // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
            .D1(1'b1),
            .D2(1'b0),
            .D3(1'b1),
            .D4(1'b0),
            .D5(1'b1),
            .D6(1'b0),
            .D7(1'b1),
            .D8(1'b0),
            .OCE(1'b1), // 1-bit input: Output data clock enable
            .RST(sync_rst), // 1-bit input: Reset
             // unused signals but were added here to make vivado happy
            .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
            .SHIFTOUT2(),
            .TBYTEOUT(), // 1-bit output: Byte group tristate
            .TFB(), // 1-bit output: 3-state control
            .TQ(), // 1-bit output: 3-state control
            // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
            .SHIFTIN1(0),
            .SHIFTIN2(0),
            // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
            .T1(0),
            .T2(0),
            .T3(0),
            .T4(0),
            .TBYTEIN(0),
            // 1-bit input: Byte group tristate
            .TCE(0)
            // 1-bit input: 3-state clock enable
        );
        // End of OSERDESE2_inst instantiation

            //Delay the DQ
            // Delay resolution: 1/(32 x 2 x F REF ) = 78.125ps
            (* IODELAY_GROUP="DDR3-GROUP" *)
            `ifndef SIM_MODEL
                ODELAYE2 #(
            `else 
                ODELAYE2_model #(
            `endif
                .DELAY_SRC("ODATAIN"), // Delay input (ODATAIN, CLKIN)
                .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter to 5ps ("TRUE"), Reduced power but high jitter 9ns ("FALSE")
                .ODELAY_TYPE("FIXED"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
                .ODELAY_VALUE(CMD_ODELAY_TAP), // Output delay tap setting (0-31)
                .REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0).
                .SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
            )
            ODELAYE2_clk (
                .CNTVALUEOUT(), // 5-bit output: Counter value output
                .DATAOUT(ddr3_clk_delayed), // 1-bit output: Delayed data/clock output
                .C(i_controller_clk), // 1-bit input: Clock input, when using OSERDESE2, C is connected to CLKDIV
                .CE(1'b0), // 1-bit input: Active high enable increment/decrement input
                .CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
                .CLKIN(1'b0), // 1-bit input: Clock delay input
                .CNTVALUEIN(5'd0), // 5-bit input: Counter value input
                .INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
                .LD(1'b0), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
                            // VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
                .LDPIPEEN(1'b0), // 1-bit input: Enables the pipeline register to load data
                .ODATAIN(ddr3_clk), // 1-bit input: Output delay data input
                .REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
            );
        // if dual rank enabled, then there will be two clk
        if(DUAL_RANK_DIMM) begin
            // OBUFDS: Differential Output Buffer
            // 7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                OBUFDS OBUFDS0_inst (
            `else 
                OBUFDS_model OBUFDS0_inst (
            `endif
                .O(o_ddr3_clk_p[0]), // Diff_p output (connect directly to top-level port)
                .OB(o_ddr3_clk_n[0]), // Diff_n output (connect directly to top-level port)
                .I(ddr3_clk_delayed) // Buffer input
            );
            `ifndef SIM_MODEL
                OBUFDS OBUFDS1_inst (
            `else 
                OBUFDS_model OBUFDS1_inst (
            `endif
                .O(o_ddr3_clk_p[1]), // Diff_p output (connect directly to top-level port)
                .OB(o_ddr3_clk_n[1]), // Diff_n output (connect directly to top-level port)
                .I(ddr3_clk_delayed) // Buffer input
            );
            // End of OBUFDS_inst instantiation
        end
        else begin
            // OBUFDS: Differential Output Buffer
            // 7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                OBUFDS OBUFDS_inst (
            `else 
                OBUFDS_model OBUFDS_inst (
            `endif
                .O(o_ddr3_clk_p), // Diff_p output (connect directly to top-level port)
                .OB(o_ddr3_clk_n), // Diff_n output (connect directly to top-level port)
                .I(ddr3_clk_delayed) // Buffer input
            );
            // End of OBUFDS_inst instantiation
        end
    end
    else begin //ODELAY is not supported

                // if dual rank enabled, then there will be two clk
        if(DUAL_RANK_DIMM) begin
            // OBUFDS: Differential Output Buffer
            // 7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                OBUFDS OBUFDS0_inst (
            `else 
                OBUFDS_model OBUFDS0_inst (
            `endif
                .O(o_ddr3_clk_p[1]), // Diff_p output (connect directly to top-level port)
                .OB(o_ddr3_clk_n[1]), // Diff_n output (connect directly to top-level port)
                .I(!i_ddr3_clk) // Buffer input
            );
            `ifndef SIM_MODEL
                OBUFDS OBUFDS1_inst (
            `else 
                OBUFDS_model OBUFDS1_inst (
            `endif
                .O(o_ddr3_clk_p[0]), // Diff_p output (connect directly to top-level port)
                .OB(o_ddr3_clk_n[0]), // Diff_n output (connect directly to top-level port)
                .I(!i_ddr3_clk) // Buffer input
            );
            // End of OBUFDS_inst instantiation
        end
        else begin
            // OBUFDS: Differential Output Buffer
            // 7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                OBUFDS OBUFDS_inst (
            `else 
                OBUFDS_model OBUFDS_inst (
            `endif
                .O(o_ddr3_clk_p), // Diff_p output (connect directly to top-level port)
                .OB(o_ddr3_clk_n), // Diff_n output (connect directly to top-level port)
                .I(!i_ddr3_clk) // Buffer input
            );
        end
    end
    
    
    // PHY data and dm
    generate
        // data: oserdes -> odelay -> iobuf
        for(gen_index = 0; gen_index < (DQ_BITS*LANES); gen_index = gen_index + 1) begin
            
            if (ODELAY_SUPPORTED) begin
                // OSERDESE2: Output SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OSERDESE2 #(
                `else 
                    OSERDESE2_model #(
                `endif
                    .DATA_RATE_OQ("DDR"), // DDR, SDR
                    .DATA_RATE_TQ("BUF"), // DDR, SDR
                    .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                    .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
                    .TRISTATE_WIDTH(1)
                )
                OSERDESE2_data(
                    .OFB(oserdes_data[gen_index]), // 1-bit output: Feedback path for data
                    .OQ(), // 1-bit output: Data path output
                    .TQ(oserdes_dq_tri_control[gen_index]),
                    .CLK(i_ddr3_clk), // 1-bit input: High speed clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                    .D1(i_controller_data[gen_index + (DQ_BITS*LANES)*0]),
                    .D2(i_controller_data[gen_index + (DQ_BITS*LANES)*1]),
                    .D3(i_controller_data[gen_index + (DQ_BITS*LANES)*2]),
                    .D4(i_controller_data[gen_index + (DQ_BITS*LANES)*3]),
                    .D5(i_controller_data[gen_index + (DQ_BITS*LANES)*4]),
                    .D6(i_controller_data[gen_index + (DQ_BITS*LANES)*5]),
                    .D7(i_controller_data[gen_index + (DQ_BITS*LANES)*6]),
                    .D8(i_controller_data[gen_index + (DQ_BITS*LANES)*7]),
                    .T1(i_controller_dq_tri_control),
                    .TCE(1'b1),
                    .OCE(1'b1), // 1-bit input: Output data clock enable
                    .RST(sync_rst), // 1-bit input: Reset
                    // unused signals but were added here to make vivado happy
                    .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                    .SHIFTOUT2(),
                    .TBYTEOUT(), // 1-bit output: Byte group tristate
                    .TFB(), // 1-bit output: 3-state control
                    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                    .SHIFTIN1(0),
                    .SHIFTIN2(0),
                    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                    .T2(0),
                    .T3(0),
                    .T4(0),
                    .TBYTEIN(0)
                    // 1-bit input: Byte group tristate
                );
                // End of OSERDESE2_inst instantiation
            
                // ODELAYE2: Output Fixed or Variable Delay Element
                // 7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                //odelay adds an insertion delay of 600ps to the actual delay setting: https://support.xilinx.com/s/article/42133?language=en_US
                //Delay the DQ
                // Delay resolution: 1/(32 x 2 x F REF ) = 78.125ps
                (* IODELAY_GROUP="DDR3-GROUP" *)
                `ifndef SIM_MODEL
                    ODELAYE2 #(
                `else 
                    ODELAYE2_model #(
                `endif
                    .DELAY_SRC("ODATAIN"), // Delay input (ODATAIN, CLKIN)
                    .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter to 5ps ("TRUE"), Reduced power but high jitter 9ns ("FALSE")
                    .ODELAY_TYPE("VAR_LOAD"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
                    .ODELAY_VALUE(DATA_ODELAY_TAP), // Output delay tap setting (0-31)
                    .REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0).
                    .SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
                )
                ODELAYE2_data (
                    .CNTVALUEOUT(), // 5-bit output: Counter value output
                    .DATAOUT(odelay_data[gen_index]), // 1-bit output: Delayed data/clock output
                    .C(i_controller_clk), // 1-bit input: Clock input, when using OSERDESE2, C is connected to CLKDIV
                    .CE(1'b0), // 1-bit input: Active high enable increment/decrement input
                    .CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
                    .CLKIN(1'b0), // 1-bit input: Clock delay input
                    .CNTVALUEIN(i_controller_odelay_data_cntvaluein), // 5-bit input: Counter value input
                    .INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
                    .LD(i_controller_odelay_data_ld[gen_index/8]), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
                                // VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
                    .LDPIPEEN(1'b0), // 1-bit input: Enables the pipeline register to load data
                    .ODATAIN(oserdes_data[gen_index]), // 1-bit input: Output delay data input
                    .REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
                );

                if(USE_IO_TERMINATION) begin 
                    // IOBUF_DCIEN: Single-ended Bi-directional Buffer with Digital Controlled Impedance (DCI)
                    // and Input path enable/disable
                    // May only be placed in High Performance (HP) Banks
                    // 7 Series
                    // Xilinx HDL Libraries Guide, version 13.4
                    `ifndef SIM_MODEL
                        IOBUF_DCIEN #(
                    `else 
                        IOBUF_DCIEN_model #(
                    `endif
                    .IBUF_LOW_PWR("FALSE"), // Low Power - "TRUE", High Performance = "FALSE"
                    .SLEW("FAST"), // Specify the output slew rate
                    .USE_IBUFDISABLE("FALSE") // Use IBUFDISABLE function, "TRUE" or "FALSE"
                    ) IOBUF_DCIEN_data (
                        .O(read_dq[gen_index]), // Buffer output
                        .IO(io_ddr3_dq[gen_index]), // Buffer inout port (connect directly to top-level port)
                        .DCITERMDISABLE(1'b0), // DCI Termination enable input
                        .I(odelay_data[gen_index]), // Buffer input
                        .IBUFDISABLE(1'b0), // Input disable input, low=disable
                        .T(oserdes_dq_tri_control[gen_index]) // 3-state enable input, high=input, low=output
                    );
                    // End of IOBUF_DCIEN_inst instantiation

                 end
                 else begin                         
                    // IOBUF: Single-ended Bi-directional Buffer
                    //All devices
                    // Xilinx HDL Libraries Guide, version 13.4
                    `ifndef SIM_MODEL
                        IOBUF #(
                    `else 
                        IOBUF_model #(
                    `endif
                        //.DRIVE(12), // Specify the output drive strength
                        .IBUF_LOW_PWR("FALSE"), // Low Power - "TRUE", High Performance = "FALSE"
                        //.IOSTANDARD("SSTL15"), // Specify the I/O standard
                        .SLEW("FAST") // Specify the output slew rate
                    ) IOBUF_data (
                        .O(read_dq[gen_index]),// Buffer output
                        .IO(io_ddr3_dq[gen_index]), // Buffer inout port (connect directly to top-level port)
                        .I(odelay_data[gen_index]), // Buffer input
                        .T(oserdes_dq_tri_control[gen_index]) // 3-state enable input, high=read, low=write
                    );
                 end
            end
            else begin //ODELAY is not supported
                // OSERDESE2: Output SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OSERDESE2 #(
                `else 
                    OSERDESE2_model #(
                `endif
                    .DATA_RATE_OQ("DDR"), // DDR, SDR
                    .DATA_RATE_TQ("BUF"), // DDR, SDR
                    .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                    .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
                    .TRISTATE_WIDTH(1)
                )
                OSERDESE2_data(
                    .OFB(), // 1-bit output: Feedback path for data
                    .OQ(oserdes_data[gen_index]), // 1-bit output: Data path output
                    .TQ(oserdes_dq_tri_control[gen_index]),
                    .CLK(i_ddr3_clk_90), // 1-bit input: High speed clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                    .D1(i_controller_data[gen_index + (DQ_BITS*LANES)*0]),
                    .D2(i_controller_data[gen_index + (DQ_BITS*LANES)*1]),
                    .D3(i_controller_data[gen_index + (DQ_BITS*LANES)*2]),
                    .D4(i_controller_data[gen_index + (DQ_BITS*LANES)*3]),
                    .D5(i_controller_data[gen_index + (DQ_BITS*LANES)*4]),
                    .D6(i_controller_data[gen_index + (DQ_BITS*LANES)*5]),
                    .D7(i_controller_data[gen_index + (DQ_BITS*LANES)*6]),
                    .D8(i_controller_data[gen_index + (DQ_BITS*LANES)*7]),
                    .T1(i_controller_dq_tri_control),
                    .TCE(1'b1),
                    .OCE(1'b1), // 1-bit input: Output data clock enable
                    .RST(sync_rst), // 1-bit input: Reset
                    // unused signals but were added here to make vivado happy
                    .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                    .SHIFTOUT2(),
                    .TBYTEOUT(), // 1-bit output: Byte group tristate
                    .TFB(), // 1-bit output: 3-state control
                    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                    .SHIFTIN1(0),
                    .SHIFTIN2(0),
                    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                    .T2(0),
                    .T3(0),
                    .T4(0),
                    .TBYTEIN(0)
                    // 1-bit input: Byte group tristate
                );
                // End of OSERDESE2_inst instantiation
                
                // IOBUF: Single-ended Bi-directional Buffer
                //All devices
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    IOBUF #(
                `else 
                    IOBUF_model #(
                `endif
                    //.DRIVE(12), // Specify the output drive strength
                    .IBUF_LOW_PWR("FALSE"), // Low Power - "TRUE", High Performance = "FALSE"
                    //.IOSTANDARD("SSTL15"), // Specify the I/O standard
                    .SLEW("FAST") // Specify the output slew rate
                ) IOBUF_data (
                    .O(read_dq[gen_index]),// Buffer output
                    .IO(io_ddr3_dq[gen_index]), // Buffer inout port (connect directly to top-level port)
                    .I(oserdes_data[gen_index]), // Buffer input
                    .T(oserdes_dq_tri_control[gen_index]) // 3-state enable input, high=read, low=write
                );
            end

            // IDELAYE2: Input Fixed or Variable Delay Element
            // 7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            (* IODELAY_GROUP="DDR3-GROUP" *)
            `ifndef SIM_MODEL
                IDELAYE2 #(
            `else 
                IDELAYE2_model #(
            `endif
                .DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
                .HIGH_PERFORMANCE_MODE("TRUE"), //Reduced jitter ("TRUE"), Reduced power ("FALSE")
                .IDELAY_TYPE("VAR_LOAD"), //FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
                .IDELAY_VALUE(DATA_IDELAY_TAP), //Input delay tap setting (0-31)
                .PIPE_SEL("FALSE"),  //Select pipelined mode, FALSE, TRUE
                .REFCLK_FREQUENCY(200.0), //IDELAYCTRL clock input frequency in MHz (190.0-210.0).
                .SIGNAL_PATTERN("DATA") //DATA, CLOCK input signal
            )
            IDELAYE2_data (
                .CNTVALUEOUT(), // 5-bit output: Counter value output
                .DATAOUT(idelay_data[gen_index]), // 1-bit output: Delayed data output
                .C(i_controller_clk), // 1-bit input: Clock input
                .CE(1'b0), // 1-bit input: Active high enable increment/decrement input
                .CINVCTRL(1'b0),// 1-bit input: Dynamic clock inversion input
                .CNTVALUEIN(i_controller_idelay_data_cntvaluein), // 5-bit input: Counter value input
                .DATAIN(), //1-bit input: Internal delay data input
                .IDATAIN(read_dq[gen_index]), // 1-bit input: Data input from the I/O
                .INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
                .LD(i_controller_idelay_data_ld[gen_index/8]), // 1-bit input: Load IDELAY_VALUE input
                .LDPIPEEN(1'b0), // 1-bit input: Enable PIPELINE register to load data input
                .REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
            );
            // End of IDELAYE2_inst instantiation

            // End of IOBUF_inst instantiation                 
            // ISERDESE2: Input SERial/DESerializer with bitslip
            //7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                ISERDESE2 #(
            `else 
                ISERDESE2_model #(
            `endif
                .DATA_RATE("DDR"), // DDR, SDR
                .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                // INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
                .INIT_Q1(1'b0),
                .INIT_Q2(1'b0),
                .INIT_Q3(1'b0),
                .INIT_Q4(1'b0),
                .INTERFACE_TYPE("NETWORKING"), // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
                .IOBDELAY("IFD"), // NONE, BOTH, IBUF, IFD
                .NUM_CE(1),// Number of clock enables (1,2)
                .OFB_USED("FALSE"), // Select OFB path (FALSE, TRUE)
                // SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
                .SRVAL_Q1(1'b0),
                .SRVAL_Q2(1'b0),
                .SRVAL_Q3(1'b0),
                .SRVAL_Q4(1'b0)
            )
            ISERDESE2_data (
                .O(),
                // 1-bit output: Combinatorial output
                // Q1 - Q8: 1-bit (each) output: Registered data outputs
                .Q1(o_controller_iserdes_data[(DQ_BITS*LANES)*7 + gen_index]),//56
                .Q2(o_controller_iserdes_data[(DQ_BITS*LANES)*6 + gen_index]), //48
                .Q3(o_controller_iserdes_data[(DQ_BITS*LANES)*5 + gen_index]),
                .Q4(o_controller_iserdes_data[(DQ_BITS*LANES)*4 + gen_index]),   
                .Q5(o_controller_iserdes_data[(DQ_BITS*LANES)*3 + gen_index]),
                .Q6(o_controller_iserdes_data[(DQ_BITS*LANES)*2 + gen_index]),
                .Q7(o_controller_iserdes_data[(DQ_BITS*LANES)*1 + gen_index]), 
                .Q8(o_controller_iserdes_data[(DQ_BITS*LANES)*0 + gen_index]),
                // SHIFTOUT1-SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
                .SHIFTOUT1(),
                .SHIFTOUT2(),
                .BITSLIP(i_controller_bitslip[gen_index/8]),
                // 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
                // CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
                // to Q8 output ports will shift, as in a barrel-shifter operation, one
                // position every time Bitslip is invoked (DDR operation is different from
                // SDR).
                // CE1, CE2: 1-bit (each) input: Data register clock enable inputs
                .CE1(1'b1),
                .CE2(1'b1),
                .CLKDIVP(), // 1-bit input: TBD
                // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
                .CLK(i_ddr3_clk), // 1-bit input: High-speed clock
                .CLKB(!i_ddr3_clk), // 1-bit input: High-speed secondary clock
                .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                .OCLK(), // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
                // Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
                .DYNCLKDIVSEL(), // 1-bit input: Dynamic CLKDIV inversion
                .DYNCLKSEL(), // 1-bit input: Dynamic CLK/CLKB inversion
                // Input Data: 1-bit (each) input: ISERDESE2 data input ports
                .D(), // 1-bit input: Data input
                .DDLY(idelay_data[gen_index]), // 1-bit input: Serial data from IDELAYE2
                .OFB(), // 1-bit input: Data feedback from OSERDESE2
                .OCLKB(), // 1-bit input: High speed negative edge output clock
                .RST(sync_rst), // 1-bit input: Active high asynchronous reset
                // SHIFTIN1-SHIFTIN2: 1-bit (each) input: Data width expansion input ports
                .SHIFTIN1(),
                .SHIFTIN2()
            );
            // End of ISERDESE2_inst instantiation
  
        end
        
        // data mask: oserdes -> odelay -> obuf
        for(gen_index = 0; gen_index < LANES; gen_index = gen_index + 1) begin
 
            
            if(ODELAY_SUPPORTED) begin
                // OSERDESE2: Output SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OSERDESE2 #(
                `else 
                    OSERDESE2_model #(
                `endif
                    .DATA_RATE_OQ("DDR"), // DDR, SDR
                    .DATA_RATE_TQ("BUF"), // DDR, SDR
                    .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                    .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
                    .TRISTATE_WIDTH(1)
                )
                OSERDESE2_dm(
                    .OFB(oserdes_dm[gen_index]), // 1-bit output: Feedback path for data
                    .OQ(), // 1-bit output: Data path output
                    .CLK(i_ddr3_clk), // 1-bit input: High speed clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                    .D1(i_controller_dm[gen_index + LANES*0]),
                    .D2(i_controller_dm[gen_index + LANES*1]),
                    .D3(i_controller_dm[gen_index + LANES*2]),
                    .D4(i_controller_dm[gen_index + LANES*3]),
                    .D5(i_controller_dm[gen_index + LANES*4]),
                    .D6(i_controller_dm[gen_index + LANES*5]),
                    .D7(i_controller_dm[gen_index + LANES*6]),
                    .D8(i_controller_dm[gen_index + LANES*7]), 
                    .TCE(1'b0),
                    .OCE(1'b1), // 1-bit input: Output data clock enable
                    .RST(sync_rst), // 1-bit input: Reset
                     // unused signals but were added here to make vivado happy
                    .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                    .SHIFTOUT2(),
                    .TBYTEOUT(), // 1-bit output: Byte group tristate
                    .TFB(), // 1-bit output: 3-state control
                    .TQ(), // 1-bit output: 3-state control
                    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                    .SHIFTIN1(0),
                    .SHIFTIN2(0),
                    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                    .T1(0),
                    .T2(0),
                    .T3(0),
                    .T4(0),
                    .TBYTEIN(0)
                    // 1-bit input: Byte group tristate
                );
                // End of OSERDESE2_inst instantiation
            
                // ODELAYE2: Output Fixed or Variable Delay Element
                // 7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                //odelay adds an insertion delay of 600ps to the actual delay setting: https://support.xilinx.com/s/article/42133?language=en_US
                //Delay the DQ
                // Delay resolution: 1/(32 x 2 x F REF ) = 78.125ps
                (* IODELAY_GROUP="DDR3-GROUP" *)
                `ifndef SIM_MODEL
                    ODELAYE2 #(
                `else 
                    ODELAYE2_model #(
                `endif
                    .DELAY_SRC("ODATAIN"), // Delay input (ODATAIN, CLKIN)
                    .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter to 5ps ("TRUE"), Reduced power but high jitter 9ns ("FALSE")
                    .ODELAY_TYPE("VAR_LOAD"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
                    .ODELAY_VALUE(DATA_ODELAY_TAP), // Output delay tap setting (0-31)
                    .REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0).
                    .SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
                )
                ODELAYE2_dm (
                    .CNTVALUEOUT(), // 5-bit output: Counter value output
                    .DATAOUT(odelay_dm[gen_index]), // 1-bit output: Delayed data/clock output
                    .C(i_controller_clk), // 1-bit input: Clock input, when using OSERDESE2, C is connected to CLKDIV
                    .CE(1'b0), // 1-bit input: Active high enable increment/decrement input
                    .CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
                    .CLKIN(1'b0), // 1-bit input: Clock delay input
                    .CNTVALUEIN(i_controller_odelay_data_cntvaluein), // 5-bit input: Counter value input
                    .INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
                    .LD(i_controller_odelay_data_ld[gen_index]), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
                                // VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
                    .LDPIPEEN(1'b0), // 1-bit input: Enables the pipeline register to load data
                    .ODATAIN(oserdes_dm[gen_index]), // 1-bit input: Output delay data input
                    .REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
                );
                
                // OBUF: Single-ended Output Buffer
                // 7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OBUF #(
                `else 
                    OBUF_model #(
                `endif
                //.IOSTANDARD("SSTL_15"), // Specify the output I/O standard
                .SLEW("FAST") // Specify the output slew rate
                ) OBUF_dm (
                    .O(o_ddr3_dm[gen_index]), // Buffer output (connect directly to top-level port)
                    .I(odelay_dm[gen_index]) // Buffer input
                );
                // End of OBUF_inst instantiation
            end
            else begin
                // OSERDESE2: Output SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OSERDESE2 #(
                `else 
                    OSERDESE2_model #(
                `endif
                    .DATA_RATE_OQ("DDR"), // DDR, SDR
                    .DATA_RATE_TQ("BUF"), // DDR, SDR
                    .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                    .INIT_OQ(1'b0), // Initial value of OQ output (1'b0,1'b1)
                    .TRISTATE_WIDTH(1)
                )
                OSERDESE2_dm(
                    .OFB(), // 1-bit output: Feedback path for data
                    .OQ(oserdes_dm[gen_index]), // 1-bit output: Data path output
                    .CLK(i_ddr3_clk_90), // 1-bit input: High speed clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                    .D1(i_controller_dm[gen_index + LANES*0]),
                    .D2(i_controller_dm[gen_index + LANES*1]),
                    .D3(i_controller_dm[gen_index + LANES*2]),
                    .D4(i_controller_dm[gen_index + LANES*3]),
                    .D5(i_controller_dm[gen_index + LANES*4]),
                    .D6(i_controller_dm[gen_index + LANES*5]),
                    .D7(i_controller_dm[gen_index + LANES*6]),
                    .D8(i_controller_dm[gen_index + LANES*7]), 
                    .TCE(1'b0),
                    .OCE(1'b1), // 1-bit input: Output data clock enable
                    .RST(sync_rst), // 1-bit input: Reset
                     // unused signals but were added here to make vivado happy
                    .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                    .SHIFTOUT2(),
                    .TBYTEOUT(), // 1-bit output: Byte group tristate
                    .TFB(), // 1-bit output: 3-state control
                    .TQ(), // 1-bit output: 3-state control
                    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                    .SHIFTIN1(0),
                    .SHIFTIN2(0),
                    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                    .T1(0),
                    .T2(0),
                    .T3(0),
                    .T4(0),
                    .TBYTEIN(0)
                    // 1-bit input: Byte group tristate
                );
                // End of OSERDESE2_inst instantiation
            
                // OBUF: Single-ended Output Buffer
                // 7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OBUF #(
                `else 
                    OBUF_model #(
                `endif
                //.IOSTANDARD("SSTL_15"), // Specify the output I/O standard
                .SLEW("FAST") // Specify the output slew rate
                ) OBUF_dm (
                    .O(o_ddr3_dm[gen_index]), // Buffer output (connect directly to top-level port)
                    .I(oserdes_dm[gen_index]) // Buffer input
                );
                // End of OBUF_inst instantiation
            
            end
        end

        // dqs: odelay -> iobuf
        for(gen_index = 0; gen_index < LANES; gen_index = gen_index + 1) begin
`ifndef DEBUG_DQS  //remove the logic for dqs if needs to be debugged (directed to output instead)  
            if(ODELAY_SUPPORTED) begin 
                // OSERDESE2: Output SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OSERDESE2 #(
                `else 
                    OSERDESE2_model #(
                `endif
                    .DATA_RATE_OQ("DDR"), // DDR, SDR
                    .DATA_RATE_TQ("BUF"), // DDR, SDR
                    .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                    .INIT_OQ(1'b1), // Initial value of OQ output (1'b0,1'b1)
                    .TRISTATE_WIDTH(1)
                )
                OSERDESE2_dqs(
                    .OFB(oserdes_dqs[gen_index]), // 1-bit output: Feedback path for data
                    .OQ(), // 1-bit output: Data path output
                    .TQ(oserdes_dqs_tri_control[gen_index]),
                    .CLK(i_ddr3_clk), // 1-bit input: High speed clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                    .D1(1'b1 && (i_controller_toggle_dqs || toggle_dqs_q)), //the last part will still have half dqs series
                    .D2(1'b0 && (i_controller_toggle_dqs || toggle_dqs_q)),
                    .D3(1'b1 && (i_controller_toggle_dqs || toggle_dqs_q) && !i_controller_write_leveling_calib),
                    .D4(1'b0 && (i_controller_toggle_dqs || toggle_dqs_q)),
                    .D5(1'b1 && i_controller_toggle_dqs && !i_controller_write_leveling_calib),
                    .D6(1'b0 && i_controller_toggle_dqs),
                    .D7(1'b1 && i_controller_toggle_dqs && !i_controller_write_leveling_calib),
                    .D8(1'b0 && i_controller_toggle_dqs),
                    .T1(i_controller_dqs_tri_control),
                    .TCE(1'b1),
                    .OCE(1'b1), // 1-bit input: Output data clock enable
                    .RST(sync_rst), // 1-bit input: Reset
                     // unused signals but were added here to make vivado happy
                    .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                    .SHIFTOUT2(),
                    .TBYTEOUT(), // 1-bit output: Byte group tristate
                    .TFB(), // 1-bit output: 3-state control
                    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                    .SHIFTIN1(0),
                    .SHIFTIN2(0),
                    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                    .T2(0),
                    .T3(0),
                    .T4(0),
                    .TBYTEIN(0)
                    // 1-bit input: Byte group tristate
                );
                // End of OSERDESE2_inst instantiation
            
            
                // ODELAYE2: Output Fixed or Variable Delay Element
                // 7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                //Delay the DQ
                (* IODELAY_GROUP="DDR3-GROUP" *)
                `ifndef SIM_MODEL
                    ODELAYE2 #(
                `else 
                    ODELAYE2_model #(
                `endif
                    .DELAY_SRC("ODATAIN"), // Delay input (ODATAIN, CLKIN)
                    .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
                    .ODELAY_TYPE("VAR_LOAD"), // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
                    .ODELAY_VALUE(DQS_ODELAY_TAP), // delay to align odelay_dqs to oserdes_dqs due to 600ps insertion delay: (1/800MHz - 600ps)/78.125ps = 8.32 taps
                    .REFCLK_FREQUENCY(200.0), // IDELAYCTRL clock input frequency in MHz (190.0-210.0).
                    .SIGNAL_PATTERN("DATA") // DATA, CLOCK input signal
                )
                ODELAYE2_dqs (
                    .CNTVALUEOUT(), // 5-bit output: Counter value output
                    .DATAOUT(odelay_dqs[gen_index]), // 1-bit output: Delayed data/clock output
                    .C(i_controller_clk), // 1-bit input: Clock input, when using OSERDESE2, C is connected to CLKDIV
                    .CE(1'b0), // 1-bit input: Active high enable increment/decrement input
                    .CINVCTRL(1'b0), // 1-bit input: Dynamic clock inversion input
                    .CLKIN(1'b0), // 1-bit input: Clock delay input
                    .CNTVALUEIN(i_controller_odelay_dqs_cntvaluein), // 5-bit input: Counter value input
                    .INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
                    .LD(i_controller_odelay_dqs_ld[gen_index]), // 1-bit input: Loads ODELAY_VALUE tap delay in VARIABLE mode, in VAR_LOAD or
                                // VAR_LOAD_PIPE mode, loads the value of CNTVALUEIN
                    .LDPIPEEN(1'b0), // 1-bit input: Enables the pipeline register to load data
                    .ODATAIN(oserdes_dqs[gen_index]), // 1-bit input: Output delay data input
                    .REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
                );
                
                if(USE_IO_TERMINATION) begin 
                    // IOBUFDS_DCIEN: Differential Bi-directional Buffer with Digital Controlled Impedance (DCI)
                    // and Input path enable/disable
                    // May only be placed in High Performance (HP) Banks
                    // 7 Series
                    // Xilinx HDL Libraries Guide, version 13.4
                    `ifndef SIM_MODEL
                        IOBUFDS_DCIEN #(
                    `else 
                        IOBUFDS_DCIEN_model #(
                    `endif
                        .IBUF_LOW_PWR("FALSE"), // Low Power - "TRUE", High Performance = "FALSE"
                        .SLEW("FAST"), // Specify the output slew rate
                        .USE_IBUFDISABLE("FALSE") // Use IBUFDISABLE function, "TRUE" or "FALSE"
                    ) IOBUFDS_DCIEN_inst (
                        .O(read_dqs[gen_index]),  // Buffer output
                        .IO(io_ddr3_dqs[gen_index]), // Diff_p inout (connect directly to top-level port)
                        .IOB(io_ddr3_dqs_n[gen_index]), // Diff_n inout (connect directly to top-level port)
                        .DCITERMDISABLE(1'b0), // DCI Termination enable input
                        .I(odelay_dqs[gen_index]), // Buffer input
                        .IBUFDISABLE(1'b0), // Input disable input, low=disable
                        .T(oserdes_dqs_tri_control[gen_index]) // 3-state enable input, high=input, low=output
                    );
                    // End of IOBUFDS_DCIEN_inst instantiation

                end
                else begin
                    // IOBUFDS: Differential Bi-directional Buffer
                    //7 Series
                    // Xilinx HDL Libraries Guide, version 13.4
                    `ifndef SIM_MODEL
                        IOBUFDS #(
                    `else 
                        IOBUFDS_model #(
                    `endif
                        //.DIFF_TERM("FALSE"), // Differential Termination ("TRUE"/"FALSE")
                        .IBUF_LOW_PWR("FALSE") // Low Power - "TRUE", High Performance = "FALSE"
                        //.IOSTANDARD("DIFF_SSTL15") // Specify the I/O standard. CONSULT WITH DATASHEET
                        //.SLEW("FAST") // Specify the output slew rate
                    ) IOBUFDS_dqs (
                        .O(read_dqs[gen_index]), // Buffer output
                        .IO(io_ddr3_dqs[gen_index]), // Diff_p inout (connect directly to top-level port)
                        .IOB(io_ddr3_dqs_n[gen_index]), // Diff_n inout (connect directly to top-level port)
                        .I(odelay_dqs[gen_index]), // Buffer input
                        .T(/*!dqs_tri_control[gen_index]*/oserdes_dqs_tri_control[gen_index]) // 3-state enable input, high=input, low=output
                    ); // End of IOBUFDS_inst instantiation
                end
                
            end
            
            else begin //ODELAY not supported
                // OSERDESE2: Output SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OSERDESE2 #(
                `else 
                    OSERDESE2_model #(
                `endif
                    .DATA_RATE_OQ("DDR"), // DDR, SDR
                    .DATA_RATE_TQ("BUF"), // DDR, SDR
                    .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                    .INIT_OQ(1'b1), // Initial value of OQ output (1'b0,1'b1)
                    .TRISTATE_WIDTH(1)
                )
                OSERDESE2_dqs(
                    .OFB(), // 1-bit output: Feedback path for data
                    .OQ(oserdes_dqs[gen_index]), // 1-bit output: Data path output
                    .TQ(oserdes_dqs_tri_control[gen_index]),
                    .CLK(!i_ddr3_clk), // 1-bit input: High speed clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                    .D1(1'b1 && (i_controller_toggle_dqs || toggle_dqs_q)), //the last part will still have half dqs series
                    .D2(1'b0 && (i_controller_toggle_dqs || toggle_dqs_q)),
                    .D3(1'b1 && (i_controller_toggle_dqs || toggle_dqs_q) && !i_controller_write_leveling_calib),
                    .D4(1'b0 && (i_controller_toggle_dqs || toggle_dqs_q)),
                    .D5(1'b1 && i_controller_toggle_dqs && !i_controller_write_leveling_calib),
                    .D6(1'b0 && i_controller_toggle_dqs),
                    .D7(1'b1 && i_controller_toggle_dqs && !i_controller_write_leveling_calib),
                    .D8(1'b0 && i_controller_toggle_dqs),
                    .T1(i_controller_dqs_tri_control),
                    .TCE(1'b1),
                    .OCE(1'b1), // 1-bit input: Output data clock enable
                    .RST(sync_rst), // 1-bit input: Reset
                     // unused signals but were added here to make vivado happy
                    .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                    .SHIFTOUT2(),
                    .TBYTEOUT(), // 1-bit output: Byte group tristate
                    .TFB(), // 1-bit output: 3-state control
                    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                    .SHIFTIN1(0),
                    .SHIFTIN2(0),
                    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                    .T2(0),
                    .T3(0),
                    .T4(0),
                    .TBYTEIN(0)
                    // 1-bit input: Byte group tristate
                );
                // End of OSERDESE2_inst instantiation
                
                // IOBUFDS: Differential Bi-directional Buffer
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    IOBUFDS #(
                `else 
                    IOBUFDS_model #(
                `endif
                    //.DIFF_TERM("FALSE"), // Differential Termination ("TRUE"/"FALSE")
                    .IBUF_LOW_PWR("FALSE") // Low Power - "TRUE", High Performance = "FALSE"
                    //.IOSTANDARD("DIFF_SSTL15") // Specify the I/O standard. CONSULT WITH DATASHEET
                    //.SLEW("FAST") // Specify the output slew rate
                ) IOBUFDS_dqs (
                    .O(read_dqs[gen_index]), // Buffer output
                    .IO(io_ddr3_dqs[gen_index]), // Diff_p inout (connect directly to top-level port)
                    .IOB(io_ddr3_dqs_n[gen_index]), // Diff_n inout (connect directly to top-level port)
                    .I(oserdes_dqs[gen_index]), // Buffer input
                    .T(/*!dqs_tri_control[gen_index]*/oserdes_dqs_tri_control[gen_index]) // 3-state enable input, high=input, low=output
                ); // End of IOBUFDS_inst instantiation
                
            end
            
            // IDELAYE2: Input Fixed or Variable Delay Element
            // 7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            (* IODELAY_GROUP="DDR3-GROUP" *)
            `ifndef SIM_MODEL
                IDELAYE2 #(
            `else 
                IDELAYE2_model #(
            `endif
                .DELAY_SRC("IDATAIN"), // Delay input (IDATAIN, DATAIN)
                .HIGH_PERFORMANCE_MODE("TRUE"), //Reduced jitter ("TRUE"), Reduced power ("FALSE")
                .IDELAY_TYPE("VAR_LOAD"), //FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
                .IDELAY_VALUE(DQS_IDELAY_TAP), //Input delay tap setting (0-31)
                .PIPE_SEL("FALSE"),  //Select pipelined mode, FALSE, TRUE
                .REFCLK_FREQUENCY(200.0), //IDELAYCTRL clock input frequency in MHz (190.0-210.0).
                .SIGNAL_PATTERN("CLOCK") //DATA, CLOCK input signal
            )
            IDELAYE2_dqs (
                .CNTVALUEOUT(), // 5-bit output: Counter value output
                .DATAOUT(idelay_dqs[gen_index]), // 1-bit output: Delayed data output
                .C(i_controller_clk), // 1-bit input: Clock input
                .CE(1'b0), // 1-bit input: Active high enable increment/decrement input
                .CINVCTRL(1'b0),// 1-bit input: Dynamic clock inversion input
                .CNTVALUEIN(i_controller_idelay_dqs_cntvaluein), // 5-bit input: Counter value input
                .DATAIN(), //1-bit input: Internal delay data input
                .IDATAIN(read_dqs[gen_index]), // 1-bit input: Data input from the I/O
                .INC(1'b0), // 1-bit input: Increment / Decrement tap delay input
                .LD(i_controller_idelay_dqs_ld[gen_index]), // 1-bit input: Load IDELAY_VALUE input
                .LDPIPEEN(1'b0), // 1-bit input: Enable PIPELINE register to load data input
                .REGRST(1'b0) // 1-bit input: Active-high reset tap-delay input
            );
            // End of IDELAYE2_inst instantiation
            
             
              // End of IOBUF_inst instantiation                 
            // ISERDESE2: Input SERial/DESerializer with bitslip
            //7 Series
            // Xilinx HDL Libraries Guide, version 13.4
            `ifndef SIM_MODEL
                ISERDESE2 #(
            `else 
                ISERDESE2_model #(
            `endif
                .DATA_RATE("DDR"), // DDR, SDR
                .DATA_WIDTH(serdes_ratio*2), // Parallel data width (2-8,10,14)
                // INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
                .INIT_Q1(1'b0),
                .INIT_Q2(1'b0),
                .INIT_Q3(1'b0),
                .INIT_Q4(1'b0),
                .INTERFACE_TYPE("NETWORKING"), // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
                .IOBDELAY("IFD"), // NONE, BOTH, IBUF, IFD
                .NUM_CE(1),// Number of clock enables (1,2)
                .OFB_USED("FALSE"), // Select OFB path (FALSE, TRUE)
                // SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
                .SRVAL_Q1(1'b0),
                .SRVAL_Q2(1'b0),
                .SRVAL_Q3(1'b0),
                .SRVAL_Q4(1'b0)
            )
            ISERDESE2_dqs (
                .O(),
                // 1-bit output: Combinatorial output
                // Q1 - Q8: 1-bit (each) output: Registered data outputs
                .Q1(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 7]),
                .Q2(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 6]),
                .Q3(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 5]),
                .Q4(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 4]),   
                .Q5(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 3]),
                .Q6(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 2]), //
                .Q7(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 1]), //3
                .Q8(o_controller_iserdes_dqs[serdes_ratio*2*gen_index + 0]), //2
                // SHIFTOUT1-SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
                .SHIFTOUT1(),
                .SHIFTOUT2(),
                .BITSLIP(i_controller_bitslip[gen_index]),
                // 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
                // CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
                // to Q8 output ports will shift, as in a barrel-shifter operation, one
                // position every time Bitslip is invoked (DDR operation is different from
                // SDR).
                // CE1, CE2: 1-bit (each) input: Data register clock enable inputs
                .CE1(1'b1),
                .CE2(1'b1),
                .CLKDIVP(), // 1-bit input: TBD
                // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
                .CLK(i_ddr3_clk), // 1-bit input: High-speed clock
                .CLKB(!i_ddr3_clk), // 1-bit input: High-speed secondary clock
                .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                .OCLK(), // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
                // Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
                .DYNCLKDIVSEL(), // 1-bit input: Dynamic CLKDIV inversion
                .DYNCLKSEL(), // 1-bit input: Dynamic CLK/CLKB inversion
                // Input Data: 1-bit (each) input: ISERDESE2 data input ports
                .D(), // 1-bit input: Data input
                .DDLY(idelay_dqs[gen_index]), // 1-bit input: Serial data from IDELAYE2
                .OFB(), // 1-bit input: Data feedback from OSERDESE2
                .OCLKB(), // 1-bit input: High speed negative edge output clock
                .RST(sync_rst), // 1-bit input: Active high asynchronous reset
                // SHIFTIN1-SHIFTIN2: 1-bit (each) input: Data width expansion input ports
                .SHIFTIN1(),
                .SHIFTIN2()
            );
            // End of ISERDESE2_inst instantiation
`endif
            
            // generate IOSERDES loopback or not for bitslip training
            if(NO_IOSERDES_LOOPBACK) begin                    
                // logic that models OSERDES loopback to ISERDES     
                // accdg to IOSELECT (UG471) for 7-series:    
                // " In DDR mode, every Bitslip operation causes the output pattern to alternate between
                //   a shift right by one and shift left by three"
                always @(posedge i_controller_clk) begin
                    if(!i_rst_n || i_controller_reset) begin
                        o_controller_iserdes_bitslip_reference_reg[(serdes_ratio*2*gen_index + 7) : (serdes_ratio*2*gen_index + 0)] <= 8'b0001_1110;
                        shift_bitslip_index[gen_index] <= 0;
                    end
                    else if(i_controller_bitslip[gen_index]) begin
                    // if shift_bitslip_index high, shift right by 3, else shift left by 1 (this is reverse of the IOSelect document for ISERDES since vector o_controller_iserdes_bitslip_reference is reversed)
                        o_controller_iserdes_bitslip_reference_reg[(serdes_ratio*2*gen_index + 7) : (serdes_ratio*2*gen_index + 0)] <= 
                            shift_bitslip_index[gen_index]? {o_controller_iserdes_bitslip_reference_reg[(serdes_ratio*2*gen_index + 2) : (serdes_ratio*2*gen_index + 0)], o_controller_iserdes_bitslip_reference_reg[(serdes_ratio*2*gen_index + 7) : (serdes_ratio*2*gen_index + 3)]}
                            : {o_controller_iserdes_bitslip_reference_reg[(serdes_ratio*2*gen_index + 6) : (serdes_ratio*2*gen_index + 0)], o_controller_iserdes_bitslip_reference_reg[(serdes_ratio*2*gen_index + 7)]};
                        shift_bitslip_index[gen_index] <= !shift_bitslip_index[gen_index];         
                    end
                end
                assign o_controller_iserdes_bitslip_reference = o_controller_iserdes_bitslip_reference_reg;
            end
            else begin // OSERDES will loopback to ISERDES for bitslip training    
                //ISERDES train
                // End of IOBUF_inst instantiation                 
                // ISERDESE2: Input SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    ISERDESE2 #(
                `else 
                    ISERDESE2_model #(
                `endif
                    .DATA_RATE("DDR"), // DDR, SDR
                    .DATA_WIDTH(serdes_ratio*2), // Parallel data width (2-8,10,14)
                    // INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
                    .INIT_Q1(1'b0),
                    .INIT_Q2(1'b0),
                    .INIT_Q3(1'b0),
                    .INIT_Q4(1'b0),
                    .INTERFACE_TYPE("NETWORKING"), // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
                    .IOBDELAY("NONE"), // NONE, BOTH, IBUF, IFD
                    .NUM_CE(1),// Number of clock enables (1,2)
                    .OFB_USED("TRUE"), // Select OFB path (FALSE, TRUE)
                    // SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
                    .SRVAL_Q1(1'b0),
                    .SRVAL_Q2(1'b0),
                    .SRVAL_Q3(1'b0),
                    .SRVAL_Q4(1'b0)
                )
                ISERDESE2_train (
                    .O(),
                    // 1-bit output: Combinatorial output
                    // Q1 - Q8: 1-bit (each) output: Registered data outputs
                    .Q1(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 7]),
                    .Q2(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 6]),
                    .Q3(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 5]),
                    .Q4(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 4]),   
                    .Q5(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 3]),
                    .Q6(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 2]),
                    .Q7(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 1]),
                    .Q8(o_controller_iserdes_bitslip_reference[gen_index*serdes_ratio*2 + 0]),
                    // SHIFTOUT1-SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
                    .SHIFTOUT1(),
                    .SHIFTOUT2(),
                    .BITSLIP(i_controller_bitslip[gen_index]),
                    // 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
                    // CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
                    // to Q8 output ports will shift, as in a barrel-shifter operation, one
                    // position every time Bitslip is invoked (DDR operation is different from
                    // SDR).
                    // CE1, CE2: 1-bit (each) input: Data register clock enable inputs
                    .CE1(1'b1),
                    .CE2(1'b1),
                    .CLKDIVP(), // 1-bit input: TBD
                    // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
                    .CLK(i_ddr3_clk), // 1-bit input: High-speed clock
                    .CLKB(!i_ddr3_clk), // 1-bit input: High-speed secondary clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    .OCLK(), // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
                    // Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
                    .DYNCLKDIVSEL(), // 1-bit input: Dynamic CLKDIV inversion
                    .DYNCLKSEL(), // 1-bit input: Dynamic CLK/CLKB inversion
                    // Input Data: 1-bit (each) input: ISERDESE2 data input ports
                    .D(), // 1-bit input: Data input
                    .DDLY(), // 1-bit input: Serial data from IDELAYE2
                    .OFB(oserdes_bitslip_reference[gen_index]), // 1-bit input: Data feedback from OSERDESE2
                    .OCLKB(), // 1-bit input: High speed negative edge output clock
                    .RST(sync_rst), // 1-bit input: Active high asynchronous reset
                    // SHIFTIN1-SHIFTIN2: 1-bit (each) input: Data width expansion input ports
                    .SHIFTIN1(),
                    .SHIFTIN2()
                );
                // End of ISERDESE2_inst instantiation
                
                // OSERDESE2: Output SERial/DESerializer with bitslip
                //7 Series
                // Xilinx HDL Libraries Guide, version 13.4
                `ifndef SIM_MODEL
                    OSERDESE2 #(
                `else 
                    OSERDESE2_model #(
                `endif
                    .DATA_RATE_OQ("DDR"), // DDR, SDR
                    .DATA_RATE_TQ("BUF"), // DDR, SDR
                    .DATA_WIDTH(8), // Parallel data width (2-8,10,14)
                    .INIT_OQ(1'b1), // Initial value of OQ output (1'b0,1'b1)
                    .TRISTATE_WIDTH(1)
                )
                OSERDESE2_train(
                    .OFB(oserdes_bitslip_reference[gen_index]), // 1-bit output: Feedback path for data
                    .OQ(), // 1-bit output: Data path output
                    .CLK(i_ddr3_clk), // 1-bit input: High speed clock
                    .CLKDIV(i_controller_clk), // 1-bit input: Divided clock
                    // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
                    .D1(1'b0),
                    .D2(1'b0),
                    .D3(1'b0),
                    .D4(1'b0),
                    .D5(1'b1),
                    .D6(1'b1),
                    .D7(1'b1),
                    .D8(1'b1),
                    .OCE(1'b1), // 1-bit input: Output data clock enable
                    .RST(sync_rst), // 1-bit input: Reset
                     // unused signals but were added here to make vivado happy
                    .SHIFTOUT1(), // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
                    .SHIFTOUT2(),
                    .TBYTEOUT(), // 1-bit output: Byte group tristate
                    .TFB(), // 1-bit output: 3-state control
                    .TQ(), // 1-bit output: 3-state control
                    // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
                    .SHIFTIN1(0),
                    .SHIFTIN2(0),
                    // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
                    .T1(0),
                    .T2(0),
                    .T3(0),
                    .T4(0),
                    .TBYTEIN(0),
                    // 1-bit input: Byte group tristate
                    .TCE(0)
                    // 1-bit input: 3-state clock enable
                );
                // End of OSERDESE2_inst instantiation  
            end
            
        end
     endgenerate 
     

    // IDELAYCTRL: IDELAYE2/ODELAYE2 Tap Delay Value Control
    // 7 Series
    // Xilinx HDL Libraries Guide, version 13.4
    (* IODELAY_GROUP="DDR3-GROUP" *)
    `ifndef SIM_MODEL
        IDELAYCTRL IDELAYCTRL_inst (
    `else 
        IDELAYCTRL_model IDELAYCTRL_inst (
    `endif
        .RDY(idelayctrl_rdy), // 1-bit output: Ready output
        .REFCLK(i_ref_clk), // 1-bit input: Reference clock input.The frequency of REFCLK must be 200 MHz to guarantee the tap-delay value specified in the applicable data sheet.
        .RST(sync_rst) // 1-bit input: Active high reset input, To ,Minimum Reset pulse width is 52ns
    );
    // End of IDELAYCTRL_inst instantiation


    // DCIRESET: Digitially Controlled Impedence Reset Component
    //7 Series
    // Xilinx HDL Libraries Guide, version 13.4
    // DCIRESET DCIRESET_inst (
    //     .LOCKED(dci_locked), // 1-bit output: LOCK status output (When low, DCI I/O impedance is being calibrated and DCI I/Os are unavailable)
    //     .RST(sync_rst) // 1-bit input: Active-high asynchronous reset input
    // );
    assign dci_locked = 1;
    // End of DCIRESET_inst instantiation
         
endmodule
