module translation_layer_128
  #(
    parameter C_ALTERA = 1'b1,
    parameter C_PCI_DATA_WIDTH = 10'd128,
    parameter C_RX_READY_LATENCY = 3'd2,
    parameter C_TX_READY_LATENCY = 3'd2
    )
   (	
        input                              CLK,
	    input                              RST_IN,

        // Xilinx Signals
	    input [C_PCI_DATA_WIDTH-1:0]       M_AXIS_RX_TDATA,
	    input [(C_PCI_DATA_WIDTH/8)-1:0]   M_AXIS_RX_TKEEP,
	    input                              M_AXIS_RX_TLAST, // Not used in the 128 bit interface
	    input                              M_AXIS_RX_TVALID,
	    output                             M_AXIS_RX_TREADY,
	    input [(C_PCI_DATA_WIDTH/32):0]    IS_SOF,
	    input [(C_PCI_DATA_WIDTH/32):0]    IS_EOF,
	    input                              RERR_FWD,
   
	    output [C_PCI_DATA_WIDTH-1:0]      S_AXIS_TX_TDATA,
	    output [(C_PCI_DATA_WIDTH/8)-1:0]  S_AXIS_TX_TKEEP,
	    output                             S_AXIS_TX_TLAST,
	    output                             S_AXIS_TX_TVALID,
	    output                             S_AXIS_SRC_DSC,
	    input                              S_AXIS_TX_TREADY,
   
	    input [15:0]                       COMPLETER_ID,
	    input                              CFG_BUS_MSTR_ENABLE, 
	    input [5:0]                        CFG_LINK_WIDTH, // cfg_lstatus[9:4] (from Link Status Register): 000001=x1, 000010=x2, 000100=x4, 001000=x8, 001100=x12, 010000=x16, 100000=x32, others=? 
	    input [1:0]                        CFG_LINK_RATE, // cfg_lstatus[1:0] (from Link Status Register): 01=2.5GT/s, 10=5.0GT/s, others=?
	    input [2:0]                        CFG_MAX_READ_REQUEST_SIZE, // cfg_dcommand[14:12] (from Device Control Register): 000=128B, 001=256B, 010=512B, 011=1024B, 100=2048B, 101=4096B
	    input [2:0]                        CFG_MAX_PAYLOAD_SIZE, // cfg_dcommand[7:5] (from Device Control Register): 000=128B, 001=256B, 010=512B, 011=1024B

	    input                              CFG_INTERRUPT_MSIEN, // 1 if MSI interrupts are enable, 0 if only legacy are supported
	    input                              CFG_INTERRUPT_RDY, // High when interrupt is able to be sent
	    output                             CFG_INTERRUPT, // High to request interrupt, when both CFG_INTERRUPT_RDY and CFG_INTERRUPT are high, interrupt is sent)
		input 				  RCB,
		input [11:0] 			  MAX_RC_CPLD, // Receive credit limit for data (be sure fc_sel == 001)
		input [7:0] 			  MAX_RC_CPLH, // Receive credit limit for headers (be sure fc_sel == 001)

        // Altera Signals
        input [C_PCI_DATA_WIDTH-1:0]       RX_ST_DATA,
        input [0:0]                        RX_ST_EOP,
        input [0:0]                        RX_ST_VALID,
        output                             RX_ST_READY,
        input [0:0]                        RX_ST_SOP,
        input [0:0]                        RX_ST_EMPTY,

        output [C_PCI_DATA_WIDTH-1:0]      TX_ST_DATA,
        output [0:0]                       TX_ST_VALID,
        input                              TX_ST_READY,
        output [0:0]                       TX_ST_EOP,
        output [0:0]                       TX_ST_SOP,
		output [0:0]                       TX_ST_EMPTY,

        input [31:0]                       TL_CFG_CTL,
        input [3:0]                        TL_CFG_ADD,
        input [52:0]                       TL_CFG_STS,

		input [7:0] 			  KO_CPL_SPC_HEADER,
		input [11:0] 			  KO_CPL_SPC_DATA,
        input                              APP_MSI_ACK,
        output                             APP_MSI_REQ,

        // Unified Signals
	    output [C_PCI_DATA_WIDTH-1:0]      RX_DATA,
	    output                             RX_DATA_VALID,
        input                              RX_DATA_READY,

	    output                             RX_TLP_END_FLAG,
	    output [3:0]                       RX_TLP_END_OFFSET,
        output                             RX_TLP_START_FLAG,
	    output [3:0]                       RX_TLP_START_OFFSET,
	    output                             RX_TLP_ERROR_POISON,
   
	    input [C_PCI_DATA_WIDTH-1:0]       TX_DATA,
	    input [(C_PCI_DATA_WIDTH/8)-1:0]   TX_DATA_BYTE_ENABLE,
	    input                              TX_TLP_END_FLAG,
        input                              TX_TLP_START_FLAG,
	    input                              TX_DATA_VALID,
	    input                              TX_TLP_ERROR_POISON, 
	    output                             TX_DATA_READY,

	    output [15:0]                      CONFIG_COMPLETER_ID,
	    output                             CONFIG_BUS_MASTER_ENABLE, 
	    output [5:0]                       CONFIG_LINK_WIDTH, // cfg_lstatus[9:4] (from Link Status Register): 000001=x1, 000010=x2, 000100=x4, 001000=x8, 001100=x12, 010000=x16, 100000=x32, others=? 
	    output [1:0]                       CONFIG_LINK_RATE, // cfg_lstatus[1:0] (from Link Status Register): 01=2.5GT/s, 10=5.0GT/s, others=?
	    output [2:0]                       CONFIG_MAX_READ_REQUEST_SIZE, // cfg_dcommand[14:12] (from Device Control Register): 000=128B, 001=256B, 010=512B, 011=1024B, 100=2048B, 101=4096B
	    output [2:0]                       CONFIG_MAX_PAYLOAD_SIZE, // cfg_dcommand[7:5] (from Device Control Register): 000=128B, 001=256B, 010=512B, 011=1024B
        output                             CONFIG_INTERRUPT_MSIENABLE, // 1 if MSI interrupts are enable, 0 if only legacy are supported
		output [11:0] 			  CONFIG_MAX_CPL_DATA, // Receive credit limit for data
		output [7:0] 			  CONFIG_MAX_CPL_HDR, // Receive credit limit for headers
		output 				  CONFIG_CPL_BOUNDARY_SEL, // Read completion boundary (0=64 bytes, 1=128 byt
   
	    output                             INTR_MSI_RDY,
	    input                              INTR_MSI_REQUEST
        );
   generate
      if(C_ALTERA == 1'b1) begin : altera_translator_128
         wire [2:0] wFMT;  // Format field of the TLP Header
         wire [4:0] wType; // Type field of the TLP header 
         wire [9:0] wLength; // Length field of the TLP Header
         wire       wLenEven; // 1 if even number of TLP data words, else 0
         wire       wQWA4DWH, wQWA3DWH;
         wire       wMsgType;
         wire       wEP;
         wire       w3DWH;
         wire       w4DWH;
         
         reg        _r1CyTLP,r1CyTLP;
         reg        _rEven4DWH, rEven4DWH;
         reg        _rEven3DWH, rEven3DWH;
         reg        _rEvenMsg, rEvenMsg;
         reg        _rEP, rEP;
         reg        _rMSG, rMSG/* synthesis noprune */;

         reg [3:0]  rTlCfgAdd,_rTlCfgAdd;
         reg [31:0] rTlCfgCtl,_rTlCfgCtl;
         reg [52:0] rTlCfgSts,_rTlCfgSts;

         reg [15:0] rCfgCompleterId;
         reg        rCfgBusMstrEnable;
         reg [2:0]  rCfgMaxReadRequestSize;
         reg [2:0]  rCfgMaxPayloadSize;
         reg        rCfgInterruptMsienable;
	 	 reg 	    rReadCompletionBoundarySel;

         reg [1:0]  _rTlpEndOffset_I, rTlpEndOffset_I;
         reg [1:0]  _rTlpEndOffset_D, rTlpEndOffset_D;

         reg [127:0] rRxStData;
         reg                          rRxStValid;
         reg                          rRxStEop;
         reg                          rRxStSop;

         assign wEP = RX_ST_DATA[14];
         assign wLength = RX_ST_DATA[9:0];
         assign wType = RX_ST_DATA[28:24];
         assign wFMT = RX_ST_DATA[31:29];
         assign wQWA3DWH = ~RX_ST_DATA[66];
         assign wQWA4DWH = ~RX_ST_DATA[98];

         assign w4DWH = wFMT[0];
         assign w3DWH = ~wFMT[0];
         assign wLenEven = ~wLength[0];
         assign wMsgType = wType[4];

         // Pre-calculating TLP End offset
         always @(*) begin
            // We expect to recieve three different types of packets
            // 3 DWH Packets, 4 DWH Packets, and Messages (No address)
            _rEven4DWH = w4DWH & ((wQWA4DWH & wLenEven) | (~wQWA4DWH & ~wLenEven));
            _rEven3DWH = w3DWH & ((wQWA3DWH & wLenEven) | (~wQWA3DWH & ~wLenEven));
            _rEvenMsg = wMsgType & wLenEven;
            _rMSG = (RX_ST_SOP & RX_ST_DATA[28]) | (rMSG & ~RX_ST_EOP);
            _rEP = (wEP & RX_ST_SOP) | (rEP & ~RX_TLP_END_FLAG); 
            _rTlpEndOffset_I = {1'b1, ((~wLenEven & w3DWH)| w4DWH)};
            _rTlpEndOffset_D = {~RX_ST_EMPTY,rEven4DWH|rEven3DWH|rEvenMsg};
            _r1CyTLP = RX_ST_SOP & RX_ST_EOP;

            _rTlCfgCtl = TL_CFG_CTL;
            _rTlCfgAdd = TL_CFG_ADD;
            _rTlCfgSts = TL_CFG_STS;
         end

         always @(posedge CLK) begin // Should be the same clock as pld_clk
            rTlCfgAdd <= _rTlCfgAdd;
            rTlCfgCtl <= _rTlCfgCtl;
            rTlCfgSts <= _rTlCfgSts;

            rEP <= _rEP;
            rTlpEndOffset_I <= _rTlpEndOffset_I;
            rTlpEndOffset_D <= _rTlpEndOffset_D;
            r1CyTLP <= _r1CyTLP;
            
            rRxStData <= RX_ST_DATA;
            rRxStValid <= RX_ST_VALID;
            rRxStEop <= RX_ST_EOP;
            rRxStSop <= RX_ST_SOP;
            

            rMSG <= _rMSG;
            
            if(RX_ST_SOP) begin
               rEven4DWH <= _rEven4DWH;
               rEven3DWH <= _rEven3DWH;
               rEvenMsg <= _rEvenMsg;
            end
            if(rTlCfgAdd == 4'h0) begin
               rCfgMaxReadRequestSize <= rTlCfgCtl[30:28];
               rCfgMaxPayloadSize <= rTlCfgCtl[23:21];
            end
	    
	    	if(rTlCfgAdd == 4'h2) begin
				rReadCompletionBoundarySel <= rTlCfgCtl[19];
            end
            if(rTlCfgAdd == 4'h3) begin
               rCfgBusMstrEnable <= rTlCfgCtl[10];
            end
            if(rTlCfgAdd == 4'hD) begin
               rCfgInterruptMsienable <= rTlCfgCtl[0];
            end
            if(rTlCfgAdd == 4'hF) begin
               rCfgCompleterId <= {rTlCfgCtl[12:0],3'b0};
            end
         end // always @ (posedge CLK)
         
         // Rx Interface (To PCIe Core)
         assign RX_ST_READY = RX_DATA_READY;

         // Rx Interface (From PCIe Core)
         assign RX_DATA = rRxStData;
         assign RX_DATA_VALID = rRxStValid;

         assign RX_TLP_END_FLAG = rRxStEop;
         assign RX_TLP_END_OFFSET = {r1CyTLP?rTlpEndOffset_I:rTlpEndOffset_D,2'b11};
         assign RX_TLP_START_FLAG = rRxStSop;
         assign RX_TLP_START_OFFSET = 4'h0000; 
         assign RX_TLP_ERROR_POISON = rEP; 

         // Configuration Interface
         assign CONFIG_COMPLETER_ID = rCfgCompleterId; 
         assign CONFIG_BUS_MASTER_ENABLE = rCfgBusMstrEnable;
         assign CONFIG_LINK_WIDTH = rTlCfgSts[40:35]; 
         assign CONFIG_LINK_RATE = rTlCfgSts[32:31];
         assign CONFIG_MAX_READ_REQUEST_SIZE = rCfgMaxReadRequestSize;
         assign CONFIG_MAX_PAYLOAD_SIZE = rCfgMaxPayloadSize;
         assign CONFIG_INTERRUPT_MSIENABLE = rCfgInterruptMsienable;
	     assign CONFIG_CPL_BOUNDARY_SEL = rReadCompletionBoundarySel;
	     assign CONFIG_MAX_CPL_HDR = KO_CPL_SPC_HEADER;
	     assign CONFIG_MAX_CPL_DATA = KO_CPL_SPC_DATA;

         // Interrupt interface 
         assign APP_MSI_REQ = INTR_MSI_REQUEST;
         assign INTR_MSI_RDY = APP_MSI_ACK; 

         tx_qword_aligner_128
           #(
             .C_ALTERA(C_ALTERA),
             .C_PCI_DATA_WIDTH(C_PCI_DATA_WIDTH),
             .C_TX_READY_LATENCY(C_TX_READY_LATENCY)
             )
         aligner_inst
           (
            // Outputs
            .TX_DATA_READY              (TX_DATA_READY),
            .TX_ST_DATA                 (TX_ST_DATA[C_PCI_DATA_WIDTH-1:0]),
            .TX_ST_VALID                (TX_ST_VALID[0:0]),
            .TX_ST_EOP                  (TX_ST_EOP[0:0]),
            .TX_ST_SOP                  (TX_ST_SOP[0:0]),
            .TX_ST_EMPTY                (TX_ST_EMPTY),
            // Inputs
            .CLK                        (CLK),
            .RST_IN                     (RST_IN),
            .TX_DATA                    (TX_DATA[C_PCI_DATA_WIDTH-1:0]),
            .TX_DATA_VALID              (TX_DATA_VALID),
            .TX_TLP_END_FLAG            (TX_TLP_END_FLAG),
            .TX_TLP_START_FLAG          (TX_TLP_START_FLAG),
            .TX_ST_READY                (TX_ST_READY));         


      end else begin : xilinx_translator_128
         // Rx Interface (From PCIe Core)
         assign RX_DATA = M_AXIS_RX_TDATA;
         assign RX_DATA_VALID = M_AXIS_RX_TVALID;

         assign RX_TLP_END_FLAG = IS_EOF[4]; // Also, M_AXIS_RX_TLAST
         assign RX_TLP_END_OFFSET = IS_EOF[3:0]; 
         assign RX_TLP_START_FLAG = IS_SOF[4]; // Also, posedge M_AXIS_RX_TVALID or negedge M_AXIS_RX_TLAST and M_AXIS_RX_TVALID = 1
         assign RX_TLP_START_OFFSET = IS_SOF[3:0];
         assign RX_TLP_ERROR_POISON = RERR_FWD;
         // Rx Interface (To PCIe Core)
         assign M_AXIS_RX_TREADY =  RX_DATA_READY;

         // TX Interface (From PCIe Core)
         assign TX_DATA_READY = S_AXIS_TX_TREADY;

         // TX Interface (TO PCIe Core)
         assign S_AXIS_TX_TDATA = TX_DATA;
         assign S_AXIS_TX_TVALID = TX_DATA_VALID;
         assign S_AXIS_TX_TKEEP = TX_DATA_BYTE_ENABLE;
         assign S_AXIS_TX_TLAST = TX_TLP_END_FLAG;
         assign S_AXIS_SRC_DSC = TX_TLP_ERROR_POISON;

         // Configuration Interface
         assign CONFIG_COMPLETER_ID = COMPLETER_ID;
         assign CONFIG_BUS_MASTER_ENABLE = CFG_BUS_MSTR_ENABLE;
         assign CONFIG_LINK_WIDTH = CFG_LINK_WIDTH;
         assign CONFIG_LINK_RATE = CFG_LINK_RATE;
         assign CONFIG_MAX_READ_REQUEST_SIZE = CFG_MAX_READ_REQUEST_SIZE;
         assign CONFIG_MAX_PAYLOAD_SIZE = CFG_MAX_PAYLOAD_SIZE;
         assign CONFIG_INTERRUPT_MSIENABLE = CFG_INTERRUPT_MSIEN;
         assign CONFIG_CPL_BOUNDARY_SEL = RCB;
         assign CONFIG_MAX_CPL_DATA = MAX_RC_CPLD;
         assign CONFIG_MAX_CPL_HDR = MAX_RC_CPLH;

         // Interrupt interface
         assign CFG_INTERRUPT = INTR_MSI_REQUEST;
         assign INTR_MSI_RDY = CFG_INTERRUPT_RDY;
      end
   endgenerate
endmodule