def __init__(self):
        self.sink = sink = stream.Endpoint([("d", 32), ("k", 4)])
        self.source = source = stream.Endpoint([("data", 40)])

        # # #

        encoder = CEInserter()(Encoder(4, True))
        self.submodules += encoder

        # control
        self.comb += [
            source.valid.eq(sink.valid),
            sink.ready.eq(source.ready),
            encoder.ce.eq(source.valid & source.ready)
        ]

        # datapath
        for i in range(4):
            self.comb += [
                encoder.k[i].eq(sink.k[i]),
                encoder.d[i].eq(sink.d[8 * i:8 * (i + 1)]),
                source.data[10 * i:10 * (i + 1)].eq(encoder.output[i])
            ]
Example #2
0
    def __init__(self,
                 pll,
                 tx_pads,
                 rx_pads,
                 dual=0,
                 channel=0,
                 data_width=20,
                 tx_polarity=0,
                 rx_polarity=0):
        assert (data_width == 20)
        assert dual in [0, 1]
        assert channel in [0, 1]
        self.dual = dual
        self.channel = channel

        # TX controls
        self.tx_enable = Signal(reset=1)
        self.tx_ready = Signal()
        self.tx_inhibit = Signal()  # FIXME
        self.tx_produce_square_wave = Signal()
        self.tx_produce_pattern = Signal()
        self.tx_pattern = Signal(data_width)
        self.tx_prbs_config = Signal(2)
        self.tx_idle = Signal()

        # RX controls
        self.rx_enable = Signal(reset=1)
        self.rx_ready = Signal()
        self.rx_align = Signal(reset=1)
        self.rx_prbs_config = Signal(2)
        self.rx_prbs_errors = Signal(32)
        self.rx_idle = Signal()

        # Loopback
        self.loopback = Signal(
        )  # FIXME: reconfigure lb_ctl to 0b0001 but does not seem enough

        # # #

        self.nwords = nwords = data_width // 10

        self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(
            nwords, True))
        self.submodules.decoders = [
            ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(nwords)
        ]

        # Transceiver direct clock outputs (useful to specify clock constraints)
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        self.tx_clk_freq = pll.config["linerate"] / data_width
        self.rx_clk_freq = pll.config["linerate"] / data_width

        # Internal signals -------------------------------------------------------------------------
        rx_los = Signal()
        rx_lol = Signal()
        rx_lsm = Signal()
        rx_align = Signal()
        rx_data = Signal(20)
        rx_bus = Signal(24)

        tx_lol = Signal()
        tx_data = Signal(20)
        tx_bus = Signal(24)

        # Control/Status CDC -----------------------------------------------------------------------
        tx_produce_square_wave = Signal()
        tx_produce_pattern = Signal()
        tx_pattern = Signal(20)
        tx_prbs_config = Signal(2)

        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        self.specials += [
            MultiReg(self.tx_produce_square_wave, tx_produce_square_wave,
                     "tx"),
            MultiReg(self.tx_produce_pattern, tx_produce_pattern, "tx"),
            MultiReg(self.tx_pattern, tx_pattern, "tx"),
            MultiReg(self.tx_prbs_config, tx_prbs_config, "tx"),
        ]

        self.specials += [
            MultiReg(self.rx_align, rx_align, "rx"),
            MultiReg(self.rx_prbs_config, rx_prbs_config, "rx"),
            MultiReg(rx_los, self.rx_idle, "sys"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors, "sys"),  # FIXME
        ]

        # Clocking ---------------------------------------------------------------------------------
        self.clock_domains.cd_tx = ClockDomain()
        self.comb += self.cd_tx.clk.eq(self.txoutclk)
        self.specials += AsyncResetSynchronizer(self.cd_tx, ResetSignal("sys"))
        self.specials += MultiReg(~self.cd_tx.rst, self.tx_ready)

        self.clock_domains.cd_rx = ClockDomain()
        self.comb += self.cd_rx.clk.eq(self.rxoutclk)
        self.specials += AsyncResetSynchronizer(self.cd_rx, ResetSignal("sys"))
        self.specials += MultiReg(~self.cd_rx.rst, self.rx_ready)

        # DCU init ---------------------------------------------------------------------------------
        self.submodules.rx_init = rx_init = SerdesRXInit(
            tx_lol, rx_lol, rx_los, rx_lsm)

        # DCU instance -----------------------------------------------------------------------------
        self.serdes_params = dict(
            # ECP5's DCU parameters/signals/instance have been documented by whitequark as part of
            #             Yumewatari project: https://github.com/whitequark/Yumewatari
            #                  Copyright (C) 2018 [email protected]
            # DCU ----------------------------------------------------------------------------------
            # DCU — power management
            p_D_MACROPDB="0b1",
            p_D_IB_PWDNB="0b1",  # undocumented (required for RX)
            p_D_TXPLL_PWDNB="0b1",
            i_D_FFC_MACROPDB=1,

            # DCU — reset
            i_D_FFC_MACRO_RST=ResetSignal("sys"),
            i_D_FFC_DUAL_RST=ResetSignal("sys"),

            # DCU — clocking
            i_D_REFCLKI=pll.refclk,
            o_D_FFS_PLOL=tx_lol,
            p_D_REFCK_MODE={
                25: "0b100",
                20: "0b000",
                16: "0b010",
                10: "0b001",
                8: "0b011"
            }[pll.config["mult"]],
            p_D_TX_MAX_RATE="5.0",  # 5.0 Gbps
            p_D_TX_VCO_CK_DIV={
                32: "0b111",
                16: "0b110",
                8: "0b101",
                4: "0b100",
                2: "0b010",
                1: "0b000"
            }[1],  # DIV/1
            p_D_BITCLK_LOCAL_EN="0b1",  # Use clock from local PLL

            # DCU ­— unknown
            p_D_CMUSETBIASI=
            "0b00",  # begin undocumented (10BSER sample code used)
            p_D_CMUSETI4CPP="0d3",
            p_D_CMUSETI4CPZ="0d3",
            p_D_CMUSETI4VCO="0b00",
            p_D_CMUSETICP4P="0b01",
            p_D_CMUSETICP4Z="0b101",
            p_D_CMUSETINITVCT="0b00",
            p_D_CMUSETISCL4VCO="0b000",
            p_D_CMUSETP1GM="0b000",
            p_D_CMUSETP2AGM="0b000",
            p_D_CMUSETZGM="0b000",
            p_D_SETIRPOLY_AUX="0b01",
            p_D_SETICONST_AUX="0b01",
            p_D_SETIRPOLY_CH="0b01",
            p_D_SETICONST_CH="0b10",
            p_D_SETPLLRC="0d1",
            p_D_RG_EN="0b0",
            p_D_RG_SET="0b00",
            p_D_REQ_ISET="0b011",
            p_D_PD_ISET="0b11",  # end undocumented

            # DCU — FIFOs
            p_D_LOW_MARK=
            "0d4",  # Clock compensation FIFO low  water mark (mean=8)
            p_D_HIGH_MARK=
            "0d12",  # Clock compensation FIFO high water mark (mean=8)

            # CHX common ---------------------------------------------------------------------------
            # CHX — protocol
            p_CHX_PROTOCOL="10BSER",
            p_CHX_UC_MODE="0b1",
            p_CHX_ENC_BYPASS="******",  # Bypass 8b10b encoder
            p_CHX_DEC_BYPASS="******",  # Bypass 8b10b encoder

            # CHX receive --------------------------------------------------------------------------
            # CHX RX ­— power management
            p_CHX_RPWDNB="0b1",
            i_CHX_FFC_RXPWDNB=1,

            # CHX RX ­— reset
            i_CHX_FFC_RRST=~self.rx_enable | rx_init.rrst,
            i_CHX_FFC_LANE_RX_RST=~self.rx_enable | rx_init.lane_rx_rst,

            # CHX RX ­— input
            i_CHX_HDINP=rx_pads.p,
            i_CHX_HDINN=rx_pads.n,
            p_CHX_REQ_EN="0b0",  # Enable equalizer
            p_CHX_RX_RATE_SEL="0d10",  # Equalizer  pole position
            p_CHX_RTERM_RX={
                "5k-ohms": "0b00000",
                "80-ohms": "0b00001",
                "75-ohms": "0b00100",
                "70-ohms": "0b00110",
                "60-ohms": "0b01011",
                "50-ohms": "0b10011",
                "46-ohms": "0b11001"
            }["50-ohms"],
            p_CHX_RXIN_CM="0b11",  # CMFB (wizard value used)
            p_CHX_RXTERM_CM="0b11",  # RX Input (wizard value used)

            # CHX RX ­— clocking
            i_CHX_RX_REFCLK=pll.refclk,
            o_CHX_FF_RX_PCLK=self.rxoutclk,
            i_CHX_FF_RXI_CLK=ClockSignal("rx"),
            p_CHX_CDR_MAX_RATE="5.0",  # 5.0 Gbps
            p_CHX_RX_DCO_CK_DIV={
                32: "0b111",
                16: "0b110",
                8: "0b101",
                4: "0b100",
                2: "0b010",
                1: "0b000"
            }[1],  # DIV/1
            p_CHX_RX_GEAR_MODE="0b1",  # 1:2 gearbox
            p_CHX_FF_RX_H_CLK_EN="0b1",  # enable  DIV/2 output clock
            p_CHX_FF_RX_F_CLK_DIS="0b1",  # disable DIV/1 output clock
            p_CHX_SEL_SD_RX_CLK="0b1",  # FIFO driven by recovered clock
            p_CHX_AUTO_FACQ_EN="0b1",  # undocumented (wizard value used)
            p_CHX_AUTO_CALIB_EN="0b1",  # undocumented (wizard value used)
            p_CHX_PDEN_SEL="0b1",  # phase detector disabled on LOS
            p_CHX_DCOATDCFG="0b00",  # begin undocumented (sample code used)
            p_CHX_DCOATDDLY="0b00",
            p_CHX_DCOBYPSATD="0b1",
            p_CHX_DCOCALDIV="0b000",
            p_CHX_DCOCTLGI="0b011",
            p_CHX_DCODISBDAVOID="0b0",
            p_CHX_DCOFLTDAC="0b00",
            p_CHX_DCOFTNRG="0b001",
            p_CHX_DCOIOSTUNE="0b010",
            p_CHX_DCOITUNE="0b00",
            p_CHX_DCOITUNE4LSB="0b010",
            p_CHX_DCOIUPDNX2="0b1",
            p_CHX_DCONUOFLSB="0b100",
            p_CHX_DCOSCALEI="0b01",
            p_CHX_DCOSTARTVAL="0b010",
            p_CHX_DCOSTEP="0b11",  # end undocumented

            # CHX RX — loss of signal
            o_CHX_FFS_RLOS=rx_los,
            p_CHX_RLOS_SEL="0b1",
            p_CHX_RX_LOS_EN="0b1",
            p_CHX_RX_LOS_LVL="0b100",  # Lattice "TBD" (wizard value used)
            p_CHX_RX_LOS_CEQ="0b11",  # Lattice "TBD" (wizard value used)

            # CHX RX — loss of lock
            o_CHX_FFS_RLOL=rx_lol,

            # CHx_RXLSM? CHx_RXWA?

            # CHX RX — link state machine
            i_CHX_FFC_SIGNAL_DETECT=rx_align,
            o_CHX_FFS_LS_SYNC_STATUS=rx_lsm,
            p_CHX_ENABLE_CG_ALIGN="0b1",
            p_CHX_UDF_COMMA_MASK="0x0ff",  # compare the 8 lsbs
            p_CHX_UDF_COMMA_A="0b0000000011",  # K28.1, K28.5 and K28.7
            p_CHX_UDF_COMMA_B="0b0001111100",  # K28.1, K28.5 and K28.7
            p_CHX_CTC_BYPASS="******",  # bypass CTC FIFO
            p_CHX_MIN_IPG_CNT="0b11",  # minimum interpacket gap of 4
            p_CHX_MATCH_2_ENABLE="0b0",  # 2 character skip matching
            p_CHX_MATCH_4_ENABLE="0b0",  # 4 character skip matching
            p_CHX_CC_MATCH_1="0x000",
            p_CHX_CC_MATCH_2="0x000",
            p_CHX_CC_MATCH_3="0x000",
            p_CHX_CC_MATCH_4="0x000",

            # CHX RX — data
            **{"o_CHX_FF_RX_D_%d" % n: rx_bus[n]
               for n in range(rx_bus.nbits)},

            # CHX transmit -------------------------------------------------------------------------
            # CHX TX — power management
            p_CHX_TPWDNB="0b1",
            i_CHX_FFC_TXPWDNB=1,

            # CHX TX ­— reset
            i_D_FFC_TRST=~self.tx_enable,
            i_CHX_FFC_LANE_TX_RST=~self.tx_enable,

            # CHX TX ­— output
            o_CHX_HDOUTP=tx_pads.p,
            o_CHX_HDOUTN=tx_pads.n,
            p_CHX_TXAMPLITUDE="0d1000",  # 1000 mV
            p_CHX_RTERM_TX={
                "5k-ohms": "0b00000",
                "80-ohms": "0b00001",
                "75-ohms": "0b00100",
                "70-ohms": "0b00110",
                "60-ohms": "0b01011",
                "50-ohms": "0b10011",
                "46-ohms": "0b11001"
            }["50-ohms"],
            p_CHX_TDRV_SLICE0_CUR="0b011",  # 400 uA
            p_CHX_TDRV_SLICE0_SEL="0b01",  # main data
            p_CHX_TDRV_SLICE1_CUR="0b000",  # 100 uA
            p_CHX_TDRV_SLICE1_SEL="0b00",  # power down
            p_CHX_TDRV_SLICE2_CUR="0b11",  # 3200 uA
            p_CHX_TDRV_SLICE2_SEL="0b01",  # main data
            p_CHX_TDRV_SLICE3_CUR="0b10",  # 2400 uA
            p_CHX_TDRV_SLICE3_SEL="0b01",  # main data
            p_CHX_TDRV_SLICE4_CUR="0b00",  # 800 uA
            p_CHX_TDRV_SLICE4_SEL="0b00",  # power down
            p_CHX_TDRV_SLICE5_CUR="0b00",  # 800 uA
            p_CHX_TDRV_SLICE5_SEL="0b00",  # power down

            # CHX TX ­— clocking
            o_CHX_FF_TX_PCLK=self.txoutclk,
            i_CHX_FF_TXI_CLK=ClockSignal("tx"),
            p_CHX_TX_GEAR_MODE="0b1",  # 1:2 gearbox
            p_CHX_FF_TX_H_CLK_EN="0b1",  # enable  DIV/2 output clock
            p_CHX_FF_TX_F_CLK_DIS="0b1",  # disable DIV/1 output clock

            # CHX TX — data
            **{"i_CHX_FF_TX_D_%d" % n: tx_bus[n]
               for n in range(tx_bus.nbits)})

        # SCI Reconfiguration ----------------------------------------------------------------------
        sci_reconfig = SerDesECP5SCIReconfig(self)
        self.submodules.sci_reconfig = sci_reconfig
        self.comb += sci_reconfig.loopback.eq(self.loopback)
        self.comb += sci_reconfig.tx_idle.eq(self.tx_idle)
        self.comb += sci_reconfig.rx_polarity.eq(rx_polarity)
        self.comb += sci_reconfig.tx_polarity.eq(tx_polarity)

        # TX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(
            data_width, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.comb += [
            self.tx_prbs.i.eq(
                Cat(*[self.encoder.output[i] for i in range(nwords)])),
            If(
                tx_produce_square_wave,
                # square wave @ linerate/data_width for scope observation
                tx_data.eq(
                    Signal(data_width, reset=(1 << (data_width // 2)) - 1)
                )).Elif(tx_produce_pattern, tx_data.eq(tx_pattern)).Else(
                    tx_data.eq(self.tx_prbs.o)),
            tx_bus[0:10].eq(tx_data[0:10]),
            tx_bus[12:22].eq(tx_data[10:20]),
        ]

        # RX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(
            data_width, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors),
            rx_data[0:10].eq(rx_bus[0:10]),
            rx_data[10:20].eq(rx_bus[12:22]),
        ]
        for i in range(nwords):
            self.sync.rx += self.decoders[i].input.eq(rx_data[10 * i:10 *
                                                              (i + 1)])
        self.comb += self.rx_prbs.i.eq(rx_data)
Example #3
0
    def __init__(self,
                 pll,
                 tx_pads,
                 rx_pads,
                 sys_clk_freq,
                 data_width=20,
                 tx_buffer_enable=False,
                 rx_buffer_enable=False,
                 clock_aligner=True,
                 clock_aligner_comma=0b0101111100,
                 tx_polarity=0,
                 rx_polarity=0,
                 pll_master=True):
        assert (data_width == 20) or (data_width == 40)

        # TX controls
        self.tx_enable = Signal(reset=1)
        self.tx_ready = Signal()
        self.tx_inhibit = Signal()
        self.tx_produce_square_wave = Signal()
        self.tx_produce_pattern = Signal()
        self.tx_pattern = Signal(data_width)
        self.tx_prbs_config = Signal(2)

        # RX controls
        self.rx_enable = Signal(reset=1)
        self.rx_ready = Signal()
        self.rx_align = Signal(reset=1)
        self.rx_prbs_config = Signal(2)
        self.rx_prbs_errors = Signal(32)

        # DRP
        self.drp = DRPInterface()

        # Loopback
        self.loopback = Signal(3)

        # # #

        self.nwords = nwords = data_width // 10

        self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(
            nwords, True))
        self.decoders = [
            ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(nwords)
        ]
        self.submodules += self.decoders

        # Transceiver direct clock outputs (useful to specify clock constraints)
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        self.tx_clk_freq = pll.config["linerate"] / data_width
        self.rx_clk_freq = pll.config["linerate"] / data_width

        # Control/Status CDC
        tx_produce_square_wave = Signal()
        tx_produce_pattern = Signal()
        tx_pattern = Signal(20)
        tx_prbs_config = Signal(2)

        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        self.specials += [
            MultiReg(self.tx_produce_square_wave, tx_produce_square_wave,
                     "tx"),
            MultiReg(self.tx_produce_pattern, tx_produce_pattern, "tx"),
            MultiReg(self.tx_pattern, tx_pattern, "tx"),
            MultiReg(self.tx_prbs_config, tx_prbs_config, "tx"),
        ]

        self.specials += [
            MultiReg(self.rx_prbs_config, rx_prbs_config, "rx"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors, "sys"),  # FIXME
        ]

        # # #

        use_cpll = isinstance(pll, GTXChannelPLL)
        use_qpll = isinstance(pll, GTXQuadPLL)

        rxcdr_cfgs = {
            1: 0x03000023ff10400020,
            2: 0x03000023ff10200020,
            4: 0x03000023ff10100020,
            8: 0x03000023ff10080020,
            16: 0x03000023ff10080020,
        }

        # TX init ----------------------------------------------------------------------------------
        self.submodules.tx_init = tx_init = GTXTXInit(
            sys_clk_freq, buffer_enable=tx_buffer_enable)
        self.comb += [
            self.tx_ready.eq(tx_init.done),
            tx_init.restart.eq(~self.tx_enable)
        ]

        # RX init ----------------------------------------------------------------------------------
        self.submodules.rx_init = rx_init = GTXRXInit(
            sys_clk_freq, buffer_enable=rx_buffer_enable)
        self.comb += [
            self.rx_ready.eq(rx_init.done),
            rx_init.restart.eq(~self.rx_enable)
        ]

        # PLL ----------------------------------------------------------------------------------
        self.comb += [
            tx_init.plllock.eq(pll.lock),
            rx_init.plllock.eq(pll.lock)
        ]
        if pll_master:
            self.comb += pll.reset.eq(tx_init.pllreset)

        # DRP mux ----------------------------------------------------------------------------------
        self.submodules.drp_mux = drp_mux = DRPMux()
        drp_mux.add_interface(self.drp)

        # GTXE2_CHANNEL instance -------------------------------------------------------------------
        txdata = Signal(data_width)
        rxdata = Signal(data_width)
        self.gtx_params = dict(
            # Simulation-Only Attributes
            p_SIM_RECEIVER_DETECT_PASS="******",
            p_SIM_TX_EIDLE_DRIVE_LEVEL="X",
            p_SIM_RESET_SPEEDUP="FALSE",
            p_SIM_CPLLREFCLK_SEL="FALSE",
            p_SIM_VERSION="4.0",

            # RX Byte and Word Alignment Attributes
            p_ALIGN_COMMA_DOUBLE="FALSE",
            p_ALIGN_COMMA_ENABLE=0b1111111111,
            p_ALIGN_COMMA_WORD=2 if data_width == 20 else 4,
            p_ALIGN_MCOMMA_DET="TRUE",
            p_ALIGN_MCOMMA_VALUE=0b1010000011,
            p_ALIGN_PCOMMA_DET="TRUE",
            p_ALIGN_PCOMMA_VALUE=0b0101111100,
            p_SHOW_REALIGN_COMMA="TRUE",
            p_RXSLIDE_AUTO_WAIT=7,
            p_RXSLIDE_MODE="OFF" if rx_buffer_enable else "PCS",
            p_RX_SIG_VALID_DLY=10,

            # RX 8B/10B Decoder Attributes
            p_RX_DISPERR_SEQ_MATCH="TRUE",
            p_DEC_MCOMMA_DETECT="TRUE",
            p_DEC_PCOMMA_DETECT="TRUE",
            p_DEC_VALID_COMMA_ONLY="TRUE",

            # RX Clock Correction Attributes
            p_CBCC_DATA_SOURCE_SEL="DECODED",
            p_CLK_COR_SEQ_2_USE="FALSE",
            p_CLK_COR_KEEP_IDLE="FALSE",
            p_CLK_COR_MAX_LAT=9 if data_width == 20 else 20,
            p_CLK_COR_MIN_LAT=7 if data_width == 20 else 16,
            p_CLK_COR_PRECEDENCE="TRUE",
            p_CLK_COR_REPEAT_WAIT=0,
            p_CLK_COR_SEQ_LEN=1,
            p_CLK_COR_SEQ_1_ENABLE=0b1111,
            p_CLK_COR_SEQ_1_1=0b0100000000,
            p_CLK_COR_SEQ_1_2=0b0000000000,
            p_CLK_COR_SEQ_1_3=0b0000000000,
            p_CLK_COR_SEQ_1_4=0b0000000000,
            p_CLK_CORRECT_USE="FALSE",
            p_CLK_COR_SEQ_2_ENABLE=0b1111,
            p_CLK_COR_SEQ_2_1=0b0100000000,
            p_CLK_COR_SEQ_2_2=0b0000000000,
            p_CLK_COR_SEQ_2_3=0b0000000000,
            p_CLK_COR_SEQ_2_4=0b0000000000,

            # RX Channel Bonding Attributes
            p_CHAN_BOND_KEEP_ALIGN="FALSE",
            p_CHAN_BOND_MAX_SKEW=1,
            p_CHAN_BOND_SEQ_LEN=1,
            p_CHAN_BOND_SEQ_1_1=0b0000000000,
            p_CHAN_BOND_SEQ_1_2=0b0000000000,
            p_CHAN_BOND_SEQ_1_3=0b0000000000,
            p_CHAN_BOND_SEQ_1_4=0b0000000000,
            p_CHAN_BOND_SEQ_1_ENABLE=0b1111,
            p_CHAN_BOND_SEQ_2_1=0b0000000000,
            p_CHAN_BOND_SEQ_2_2=0b0000000000,
            p_CHAN_BOND_SEQ_2_3=0b0000000000,
            p_CHAN_BOND_SEQ_2_4=0b0000000000,
            p_CHAN_BOND_SEQ_2_ENABLE=0b1111,
            p_CHAN_BOND_SEQ_2_USE="FALSE",
            p_FTS_DESKEW_SEQ_ENABLE=0b1111,
            p_FTS_LANE_DESKEW_CFG=0b1111,
            p_FTS_LANE_DESKEW_EN="FALSE",

            # RX Margin Analysis Attributes
            p_ES_CONTROL=0b000000,
            p_ES_ERRDET_EN="FALSE",
            p_ES_EYE_SCAN_EN="TRUE",
            p_ES_HORZ_OFFSET=0x000,
            p_ES_PMA_CFG=0b0000000000,
            p_ES_PRESCALE=0b00000,
            p_ES_QUALIFIER=0x00000000000000000000,
            p_ES_QUAL_MASK=0x00000000000000000000,
            p_ES_SDATA_MASK=0x00000000000000000000,
            p_ES_VERT_OFFSET=0b000000000,

            # FPGA RX Interface Attributes
            p_RX_DATA_WIDTH=data_width,

            # PMA Attributes
            p_OUTREFCLK_SEL_INV=0b11,
            p_PMA_RSV=0x001e7080,
            p_PMA_RSV2=0x2050,
            p_PMA_RSV3=0b00,
            p_PMA_RSV4=0x00000000,
            p_RX_BIAS_CFG=0b000000000100,
            p_DMONITOR_CFG=0x000A00,
            p_RX_CM_SEL=0b11,
            p_RX_CM_TRIM=0b010,
            p_RX_DEBUG_CFG=0b000000000000,
            p_RX_OS_CFG=0b0000010000000,
            p_TERM_RCAL_CFG=0b10000,
            p_TERM_RCAL_OVRD=0b0,
            p_TST_RSV=0x00000000,
            p_RX_CLK25_DIV=5,
            p_TX_CLK25_DIV=5,
            p_UCODEER_CLR=0b0,

            # PCI Express Attributes
            p_PCS_PCIE_EN="FALSE",

            # PCS Attributes
            p_PCS_RSVD_ATTR=0x000000000000,

            # RX Buffer Attributes
            p_RXBUF_ADDR_MODE="FAST",
            p_RXBUF_EIDLE_HI_CNT=0b1000,
            p_RXBUF_EIDLE_LO_CNT=0b0000,
            p_RXBUF_EN="TRUE" if rx_buffer_enable else "FALSE",
            p_RX_BUFFER_CFG=0b000000,
            p_RXBUF_RESET_ON_CB_CHANGE="TRUE",
            p_RXBUF_RESET_ON_COMMAALIGN="FALSE",
            p_RXBUF_RESET_ON_EIDLE="FALSE",
            p_RXBUF_RESET_ON_RATE_CHANGE="TRUE",
            p_RXBUFRESET_TIME=0b00001,
            p_RXBUF_THRESH_OVFLW=61,
            p_RXBUF_THRESH_OVRD="FALSE",
            p_RXBUF_THRESH_UNDFLW=4,
            p_RXDLY_CFG=0x001F,
            p_RXDLY_LCFG=0x030,
            p_RXDLY_TAP_CFG=0x0000,
            p_RXPH_CFG=0x000000,
            p_RXPHDLY_CFG=0x084020,
            p_RXPH_MONITOR_SEL=0b00000,
            p_RX_XCLK_SEL="RXREC" if rx_buffer_enable else "RXUSR",
            p_RX_DDI_SEL=0b000000,
            p_RX_DEFER_RESET_BUF_EN="TRUE",

            # CDR Attributes
            p_RXCDR_CFG=rxcdr_cfgs[pll.config["d"]],
            p_RXCDR_FR_RESET_ON_EIDLE=0b0,
            p_RXCDR_HOLD_DURING_EIDLE=0b0,
            p_RXCDR_PH_RESET_ON_EIDLE=0b0,
            p_RXCDR_LOCK_CFG=0b010101,

            # RX Initialization and Reset Attributes
            p_RXCDRFREQRESET_TIME=0b00001,
            p_RXCDRPHRESET_TIME=0b00001,
            p_RXISCANRESET_TIME=0b00001,
            p_RXPCSRESET_TIME=0b00001,
            p_RXPMARESET_TIME=0b00011,

            # RX OOB Signaling Attributes
            p_RXOOB_CFG=0b0000110,

            # RX Gearbox Attributes
            p_RXGEARBOX_EN="FALSE",
            p_GEARBOX_MODE=0b000,

            # PRBS Detection Attribute
            p_RXPRBS_ERR_LOOPBACK=0b0,

            # Power-Down Attributes
            p_PD_TRANS_TIME_FROM_P2=0x03c,
            p_PD_TRANS_TIME_NONE_P2=0x3c,
            p_PD_TRANS_TIME_TO_P2=0x64,

            # RX OOB Signaling Attributes
            p_SAS_MAX_COM=64,
            p_SAS_MIN_COM=36,
            p_SATA_BURST_SEQ_LEN=0b0101,
            p_SATA_BURST_VAL=0b100,
            p_SATA_EIDLE_VAL=0b100,
            p_SATA_MAX_BURST=8,
            p_SATA_MAX_INIT=21,
            p_SATA_MAX_WAKE=7,
            p_SATA_MIN_BURST=4,
            p_SATA_MIN_INIT=12,
            p_SATA_MIN_WAKE=4,

            # RX Fabric Clock Output Control Attributes
            p_TRANS_TIME_RATE=0x0E,

            # TX Buffer Attributes
            p_TXBUF_EN="TRUE" if tx_buffer_enable else "FALSE",
            p_TXBUF_RESET_ON_RATE_CHANGE="TRUE",
            p_TXDLY_CFG=0x001F,
            p_TXDLY_LCFG=0x030,
            p_TXDLY_TAP_CFG=0x0000,
            p_TXPH_CFG=0x0780,
            p_TXPHDLY_CFG=0x084020,
            p_TXPH_MONITOR_SEL=0b00000,
            p_TX_XCLK_SEL="TXOUT" if tx_buffer_enable else "TXUSR",

            # FPGA TX Interface Attributes
            p_TX_DATA_WIDTH=data_width,

            # TX Configurable Driver Attributes
            p_TX_DEEMPH0=0b00000,
            p_TX_DEEMPH1=0b00000,
            p_TX_EIDLE_ASSERT_DELAY=0b110,
            p_TX_EIDLE_DEASSERT_DELAY=0b100,
            p_TX_LOOPBACK_DRIVE_HIZ="FALSE",
            p_TX_MAINCURSOR_SEL=0b0,
            p_TX_DRIVE_MODE="DIRECT",
            p_TX_MARGIN_FULL_0=0b1001110,
            p_TX_MARGIN_FULL_1=0b1001001,
            p_TX_MARGIN_FULL_2=0b1000101,
            p_TX_MARGIN_FULL_3=0b1000010,
            p_TX_MARGIN_FULL_4=0b1000000,
            p_TX_MARGIN_LOW_0=0b1000110,
            p_TX_MARGIN_LOW_1=0b1000100,
            p_TX_MARGIN_LOW_2=0b1000010,
            p_TX_MARGIN_LOW_3=0b1000000,
            p_TX_MARGIN_LOW_4=0b1000000,

            # TX Gearbox Attributes
            p_TXGEARBOX_EN="FALSE",

            # TX Initialization and Reset Attributes
            p_TXPCSRESET_TIME=0b00001,
            p_TXPMARESET_TIME=0b00001,

            # TX Receiver Detection Attributes
            p_TX_RXDETECT_CFG=0x1832,
            p_TX_RXDETECT_REF=0b100,

            # CPLL Attributes
            p_CPLL_CFG=0xBC07DC,
            p_CPLL_FBDIV=1 if use_qpll else pll.config["n2"],
            p_CPLL_FBDIV_45=4 if use_qpll else pll.config["n1"],
            p_CPLL_INIT_CFG=0x00001E,
            p_CPLL_LOCK_CFG=0x01E8,
            p_CPLL_REFCLK_DIV=1 if use_qpll else pll.config["m"],
            p_RXOUT_DIV=pll.config["d"],
            p_TXOUT_DIV=pll.config["d"],
            p_SATA_CPLL_CFG="VCO_3000MHZ",

            # RX Initialization and Reset Attributes
            p_RXDFELPMRESET_TIME=0b0001111,

            # RX Equalizer Attributes
            p_RXLPM_HF_CFG=0b00000011110000,
            p_RXLPM_LF_CFG=0b00000011110000,
            p_RX_DFE_GAIN_CFG=0x020FEA,
            p_RX_DFE_H2_CFG=0b000000000000,
            p_RX_DFE_H3_CFG=0b000001000000,
            p_RX_DFE_H4_CFG=0b00011110000,
            p_RX_DFE_H5_CFG=0b00011100000,
            p_RX_DFE_KL_CFG=0b0000011111110,
            p_RX_DFE_LPM_CFG=0x0954,
            p_RX_DFE_LPM_HOLD_DURING_EIDLE=0b0,
            p_RX_DFE_UT_CFG=0b10001111000000000,
            p_RX_DFE_VP_CFG=0b00011111100000011,

            # Power-Down Attributes
            p_RX_CLKMUX_PD=0b1,
            p_TX_CLKMUX_PD=0b1,

            # FPGA RX Interface Attribute
            p_RX_INT_DATAWIDTH=data_width == 40,

            # FPGA TX Interface Attribute
            p_TX_INT_DATAWIDTH=data_width == 40,

            # TX Configurable Driver Attributes
            p_TX_QPI_STATUS_EN=0b0,

            # RX Equalizer Attributes
            p_RX_DFE_KL_CFG2=0x301148AC,
            p_RX_DFE_XYD_CFG=0b0000000000000,

            # TX Configurable Driver Attributes
            p_TX_PREDRIVER_MODE=0b0)
        self.gtx_params.update(
            # CPLL Ports
            #o_CPLLFBCLKLOST    =,
            o_CPLLLOCK=Signal() if use_qpll else pll.lock,
            i_CPLLLOCKDETCLK=ClockSignal(),
            i_CPLLLOCKEN=1,
            i_CPLLPD=0,
            #o_CPLLREFCLKLOST   = ,
            i_CPLLREFCLKSEL=0b001,
            i_CPLLRESET=0 if use_qpll else pll.reset,
            i_GTRSVD=0b0000000000000000,
            i_PCSRSVDIN=0b0000000000000000,
            i_PCSRSVDIN2=0b00000,
            i_PMARSVDIN=0b00000,
            i_PMARSVDIN2=0b00000,
            i_TSTIN=0b11111111111111111111,
            #o_TSTOUT           =,

            # Channel
            i_CLKRSVD=0b0000,

            # Channel - Clocking Ports
            i_GTGREFCLK=0,
            i_GTNORTHREFCLK0=0,
            i_GTNORTHREFCLK1=0,
            i_GTREFCLK0=0 if use_qpll else pll.refclk,
            i_GTREFCLK1=0,
            i_GTSOUTHREFCLK0=0,
            i_GTSOUTHREFCLK1=0,

            # Channel - DRP Ports
            i_DRPADDR=drp_mux.addr,
            i_DRPCLK=drp_mux.clk,
            i_DRPDI=drp_mux.di,
            o_DRPDO=drp_mux.do,
            i_DRPEN=drp_mux.en,
            o_DRPRDY=drp_mux.rdy,
            i_DRPWE=drp_mux.we,

            # Clocking Ports
            #o_GTREFCLKMONITOR  =,
            i_QPLLCLK=0 if use_cpll else pll.clk,
            i_QPLLREFCLK=0 if use_cpll else pll.refclk,
            i_RXSYSCLKSEL=0b11 if use_qpll else 0b00,
            i_TXSYSCLKSEL=0b11 if use_qpll else 0b00,

            # Digital Monitor Ports
            #o_DMONITOROUT      =,

            # FPGA TX Interface Datapath Configuration
            i_TX8B10BEN=0,

            # Loopback Ports
            i_LOOPBACK=self.loopback,

            # PCI Express Ports
            #o_PHYSTATUS        =,
            i_RXRATE=0b000,
            #o_RXVALID          =,

            # Power-Down Ports
            i_RXPD=Cat(rx_init.gtXxpd, rx_init.gtXxpd),
            i_TXPD=0b00,

            # RX 8B/10B Decoder Ports
            i_SETERRSTATUS=0,

            # RX Initialization and Reset Ports
            i_EYESCANRESET=0,
            i_RXUSERRDY=rx_init.Xxuserrdy,

            # RX Margin Analysis Ports
            #o_EYESCANDATAERROR =,
            i_EYESCANMODE=0,
            i_EYESCANTRIGGER=0,

            # Receive Ports - CDR Ports
            i_RXCDRFREQRESET=0,
            i_RXCDRHOLD=0,
            #o_RXCDRLOCK        =,
            i_RXCDROVRDEN=0,
            i_RXCDRRESET=0,
            i_RXCDRRESETRSV=0,

            # Receive Ports - Clock Correction Ports
            #o_RXCLKCORCNT      =,

            # Receive Ports - FPGA RX Interface Datapath Configuration
            i_RX8B10BEN=0,

            # Receive Ports - FPGA RX Interface Ports
            i_RXUSRCLK=ClockSignal("rx"),
            i_RXUSRCLK2=ClockSignal("rx"),

            # Receive Ports - FPGA RX interface Ports
            o_RXDATA=Cat(*[rxdata[10 * i:10 * i + 8] for i in range(nwords)]),

            # Receive Ports - Pattern Checker Ports
            #o_RXPRBSERR        =,
            i_RXPRBSSEL=0b000,

            # Receive Ports - Pattern Checker ports
            i_RXPRBSCNTRESET=0,

            # Receive Ports - RX  Equalizer Ports
            i_RXDFEXYDEN=1,
            i_RXDFEXYDHOLD=0,
            i_RXDFEXYDOVRDEN=0,

            # Receive Ports - RX 8B/10B Decoder Ports
            i_RXDISPERR=Cat(*[rxdata[10 * i + 9] for i in range(nwords)]),
            #o_RXNOTINTABLE     =,

            # Receive Ports - RX AFE
            i_GTXRXP=rx_pads.p,
            i_GTXRXN=rx_pads.n,

            # Receive Ports - RX Buffer Bypass Ports
            i_RXBUFRESET=0,
            #o_RXBUFSTATUS      =,
            i_RXDDIEN=0 if rx_buffer_enable else 1,
            i_RXDLYBYPASS=1 if rx_buffer_enable else 0,
            i_RXDLYEN=0,
            i_RXDLYOVRDEN=0,
            i_RXDLYSRESET=rx_init.Xxdlysreset,
            o_RXDLYSRESETDONE=rx_init.Xxdlysresetdone,
            i_RXPHALIGN=0,
            o_RXPHALIGNDONE=rx_init.Xxphaligndone,
            i_RXPHALIGNEN=0,
            i_RXPHDLYPD=0,
            i_RXPHDLYRESET=0,
            #o_RXPHMONITOR      =,
            i_RXPHOVRDEN=0,
            #o_RXPHSLIPMONITOR  =,
            #o_RXSTATUS         =,

            # Receive Ports - RX Byte and Word Alignment Ports
            #o_RXBYTEISALIGNED  =,
            #o_RXBYTEREALIGN    =,
            #o_RXCOMMADET       =,
            i_RXCOMMADETEN=1,
            i_RXMCOMMAALIGNEN=(~clock_aligner & self.rx_align &
                               (rx_prbs_config == 0b00))
            if rx_buffer_enable else 0,
            i_RXPCOMMAALIGNEN=(~clock_aligner & self.rx_align &
                               (rx_prbs_config == 0b00))
            if rx_buffer_enable else 0,

            # Receive Ports - RX Channel Bonding Ports
            #o_RXCHANBONDSEQ    =,
            i_RXCHBONDEN=0,
            i_RXCHBONDLEVEL=0b000,
            i_RXCHBONDMASTER=0,
            #o_RXCHBONDO        =,
            i_RXCHBONDSLAVE=0,

            # Receive Ports - RX Channel Bonding Ports
            #o_RXCHANISALIGNED  =,
            #o_RXCHANREALIGN    =,

            # Receive Ports - RX Equailizer Ports
            i_RXLPMHFHOLD=0,
            i_RXLPMHFOVRDEN=0,
            i_RXLPMLFHOLD=0,

            # Receive Ports - RX Equalizer Ports
            i_RXDFEAGCHOLD=0,
            i_RXDFEAGCOVRDEN=0,
            i_RXDFECM1EN=0,
            i_RXDFELFHOLD=0,
            i_RXDFELFOVRDEN=1,
            i_RXDFELPMRESET=0,
            i_RXDFETAP2HOLD=0,
            i_RXDFETAP2OVRDEN=0,
            i_RXDFETAP3HOLD=0,
            i_RXDFETAP3OVRDEN=0,
            i_RXDFETAP4HOLD=0,
            i_RXDFETAP4OVRDEN=0,
            i_RXDFETAP5HOLD=0,
            i_RXDFETAP5OVRDEN=0,
            i_RXDFEUTHOLD=0,
            i_RXDFEUTOVRDEN=0,
            i_RXDFEVPHOLD=0,
            i_RXDFEVPOVRDEN=0,
            i_RXDFEVSEN=0,
            i_RXLPMLFKLOVRDEN=0,
            #o_RXMONITOROUT     =
            i_RXMONITORSEL=0,
            i_RXOSHOLD=0,
            i_RXOSOVRDEN=0,

            # Receive Ports - RX Fabric ClocK Output Control Ports
            #o_RXRATEDONE       =,

            # Receive Ports - RX Fabric Output Control Ports
            o_RXOUTCLK=self.rxoutclk,
            #o_RXOUTCLKFABRIC   =,
            #o_RXOUTCLKPCS      =,
            i_RXOUTCLKSEL=0b010,

            # Receive Ports - RX Gearbox Ports
            #o_RXDATAVALID      =,
            #o_RXHEADER         =,
            #o_RXHEADERVALID    =,
            #o_RXSTARTOFSEQ     =,

            # Receive Ports - RX Gearbox Ports
            i_RXGEARBOXSLIP=0,

            # Receive Ports - RX Initialization and Reset Ports
            i_GTRXRESET=rx_init.gtXxreset,
            i_RXOOBRESET=0,
            i_RXPCSRESET=0,
            i_RXPMARESET=0,

            # Receive Ports - RX Margin Analysis ports
            i_RXLPMEN=0,

            # Receive Ports - RX OOB Signaling ports
            #o_RXCOMSASDET      =,
            #o_RXCOMWAKEDET     =,

            # Receive Ports - RX OOB Signaling ports
            #o_RXCOMINITDET     =,

            # Receive Ports - RX OOB signalling Ports
            #o_RXELECIDLE       =,
            i_RXELECIDLEMODE=0b11,

            # Receive Ports - RX Polarity Control Ports
            i_RXPOLARITY=rx_polarity,

            # Receive Ports - RX gearbox ports
            i_RXSLIDE=0,

            # Receive Ports - RX8B/10B Decoder Ports
            #o_RXCHARISCOMMA    =,
            o_RXCHARISK=Cat(*[rxdata[10 * i + 8] for i in range(nwords)]),

            # Receive Ports - Rx Channel Bonding Ports
            i_RXCHBONDI=0b00000,

            # Receive Ports -RX Initialization and Reset Ports
            o_RXRESETDONE=rx_init.Xxresetdone,

            # Rx AFE Ports
            i_RXQPIEN=0,
            #o_RXQPISENN        =,
            #o_RXQPISENP        =,

            # TX Buffer Bypass Ports
            i_TXPHDLYTSTCLK=0,

            # TX Configurable Driver Ports
            i_TXPOSTCURSOR=0b00000,
            i_TXPOSTCURSORINV=0,
            i_TXPRECURSOR=0b00000,
            i_TXPRECURSORINV=0,
            i_TXQPIBIASEN=0,
            i_TXQPISTRONGPDOWN=0,
            i_TXQPIWEAKPUP=0,

            # TX Initialization and Reset Ports
            i_CFGRESET=0,
            i_GTTXRESET=tx_init.gtXxreset,
            #o_PCSRSVDOUT       =,
            i_TXUSERRDY=tx_init.Xxuserrdy,

            # Transceiver Reset Mode Operation
            i_GTRESETSEL=0,
            i_RESETOVRD=0,

            # Transmit Ports - 8b10b Encoder Control Ports
            i_TXCHARDISPMODE=Cat(*[txdata[10 * i + 9] for i in range(nwords)]),
            i_TXCHARDISPVAL=Cat(*[txdata[10 * i + 8] for i in range(nwords)]),

            # Transmit Ports - FPGA TX Interface Ports
            i_TXUSRCLK=ClockSignal("tx"),
            i_TXUSRCLK2=ClockSignal("tx"),

            # Transmit Ports - PCI Express Ports
            i_TXELECIDLE=0,
            i_TXMARGIN=0b000,
            i_TXRATE=0b000,
            i_TXSWING=0,

            # Transmit Ports - Pattern Generator Ports
            i_TXPRBSFORCEERR=0,

            # Transmit Ports - TX Buffer Bypass Ports
            i_TXDLYBYPASS=1 if tx_buffer_enable else 0,
            i_TXDLYEN=0,
            i_TXDLYHOLD=0,
            i_TXDLYOVRDEN=0,
            i_TXDLYSRESET=tx_init.Xxdlysreset,
            o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
            i_TXDLYUPDOWN=0,
            i_TXPHALIGN=0,
            o_TXPHALIGNDONE=tx_init.Xxphaligndone,
            i_TXPHALIGNEN=0,
            i_TXPHDLYPD=0,
            i_TXPHDLYRESET=0,
            i_TXPHINIT=0,
            #o_TXPHINITDONE     =,
            i_TXPHOVRDEN=0,

            # Transmit Ports - TX Buffer Ports
            #o_TXBUFSTATUS      =,

            # Transmit Ports - TX Configurable Driver Ports
            i_TXBUFDIFFCTRL=0b100,
            i_TXDEEMPH=0,
            i_TXDIFFCTRL=0b1000,
            i_TXDIFFPD=0,
            i_TXINHIBIT=self.tx_inhibit,
            i_TXMAINCURSOR=0b0000000,
            i_TXPISOPD=0,

            # Transmit Ports - TX Data Path interface
            i_TXDATA=Cat(*[txdata[10 * i:10 * i + 8] for i in range(nwords)]),

            # Transmit Ports - TX Driver and OOB signaling
            o_GTXTXN=tx_pads.n,
            o_GTXTXP=tx_pads.p,

            # Transmit Ports - TX Fabric Clock Output Control Ports
            o_TXOUTCLK=self.txoutclk,
            #o_TXOUTCLKFABRIC   =,
            #o_TXOUTCLKPCS      =,
            i_TXOUTCLKSEL=0b010 if tx_buffer_enable else 0b011,
            #o_TXRATEDONE       =,

            # Transmit Ports - TX Gearbox Ports
            i_TXCHARISK=0b00000000,
            #o_TXGEARBOXREADY   =,
            i_TXHEADER=0b000,
            i_TXSEQUENCE=0b0000000,
            i_TXSTARTSEQ=0,

            # Transmit Ports - TX Initialization and Reset Ports
            i_TXPCSRESET=0,
            i_TXPMARESET=0,
            o_TXRESETDONE=tx_init.Xxresetdone,

            # Transmit Ports - TX OOB signaling Ports
            #o_TXCOMFINISH      =,
            i_TXCOMINIT=0,
            i_TXCOMSAS=0,
            i_TXCOMWAKE=0,
            i_TXPDELECIDLEMODE=0,

            # Transmit Ports - TX Polarity Control Ports
            i_TXPOLARITY=tx_polarity,

            # Transmit Ports - TX Receiver Detection Ports
            i_TXDETECTRX=0,

            # Transmit Ports - TX8b/10b Encoder Ports
            i_TX8B10BBYPASS=0b00000000,

            # Transmit Ports - pattern Generator Ports
            i_TXPRBSSEL=0b000,

            # Tx Configurable Driver  Ports
            #o_TXQPISENN        =,
            #o_TXQPISENP        =,
        )

        # TX clocking ------------------------------------------------------------------------------
        tx_reset_deglitched = Signal()
        tx_reset_deglitched.attr.add("no_retiming")
        self.sync += tx_reset_deglitched.eq(~tx_init.done)
        self.clock_domains.cd_tx = ClockDomain()

        txoutclk_bufg = Signal()
        self.specials += Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_bufg)

        if not tx_buffer_enable:
            txoutclk_div = pll.config["clkin"] / self.tx_clk_freq
        else:
            txoutclk_div = 1
        # Use txoutclk_bufg when divider is 1
        if txoutclk_div == 1:
            self.comb += self.cd_tx.clk.eq(txoutclk_bufg)
            self.specials += AsyncResetSynchronizer(self.cd_tx,
                                                    tx_reset_deglitched)
        # Use a BUFR when integer divider (with BUFR_DIVIDE)
        elif txoutclk_div == int(txoutclk_div):
            txoutclk_bufr = Signal()
            self.specials += [
                Instance("BUFR",
                         i_I=txoutclk_bufg,
                         o_O=txoutclk_bufr,
                         i_CE=1,
                         p_BUFR_DIVIDE=str(int(txoutclk_div))),
                Instance("BUFG", i_I=txoutclk_bufr, o_O=self.cd_tx.clk),
                AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
            ]
        # Use a PLL when non-integer divider
        else:
            txoutclk_pll = S7PLL()
            self.comb += txoutclk_pll.reset.eq(tx_reset_deglitched)
            self.submodules += txoutclk_pll
            txoutclk_pll.register_clkin(txoutclk_bufg, pll.config["clkin"])
            txoutclk_pll.create_clkout(self.cd_tx, self.tx_clk_freq)

        # RX clocking ------------------------------------------------------------------------------
        rx_reset_deglitched = Signal()
        rx_reset_deglitched.attr.add("no_retiming")
        self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
        self.clock_domains.cd_rx = ClockDomain()
        self.specials += [
            Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
            AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
        ]

        # TX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(
            data_width, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.comb += [
            self.tx_prbs.i.eq(
                Cat(*[self.encoder.output[i] for i in range(nwords)])),
            If(
                tx_produce_square_wave,
                # square wave @ linerate/data_width for scope observation
                txdata.eq(
                    Signal(data_width, reset=(1 << (data_width // 2)) - 1)
                )).Elif(tx_produce_pattern,
                        txdata.eq(tx_pattern)).Else(txdata.eq(self.tx_prbs.o))
        ]

        # RX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(
            data_width, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        for i in range(nwords):
            self.comb += self.decoders[i].input.eq(rxdata[10 * i:10 * (i + 1)])
        self.comb += self.rx_prbs.i.eq(rxdata)

        # Clock Aligner ----------------------------------------------------------------------------
        if clock_aligner:
            clock_aligner = BruteforceClockAligner(clock_aligner_comma,
                                                   self.tx_clk_freq)
            self.submodules.clock_aligner = clock_aligner
            ps_restart = PulseSynchronizer("tx", "sys")
            self.submodules += ps_restart
            self.comb += [
                clock_aligner.rxdata.eq(rxdata),
                ps_restart.i.eq(clock_aligner.restart),
                rx_init.restart.eq((ps_restart.o & self.rx_align)
                                   | ~self.rx_enable),
                self.rx_ready.eq(clock_aligner.ready)
            ]
    def __init__(self, cpll, tx_pads, rx_pads, sys_clk_freq,
                 clock_aligner=True, internal_loopback=False,
                 tx_polarity=0, rx_polarity=0):
        self.tx_produce_square_wave = CSRStorage()
        self.tx_prbs_config = CSRStorage(2)

        self.rx_prbs_config = CSRStorage(2)
        self.rx_prbs_errors = CSRStatus(32)

        # # #

        self.submodules.encoder = ClockDomainsRenamer("tx")(
            Encoder(2, True))
        self.decoders = [ClockDomainsRenamer("rx")(
            Decoder(True)) for _ in range(2)]
        self.submodules += self.decoders

        self.rx_ready = Signal()

        # transceiver direct clock outputs
        # useful to specify clock constraints in a way palatable to Vivado
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        self.tx_clk_freq = cpll.config["linerate"]/20

        # control/status cdc
        tx_produce_square_wave = Signal()
        tx_prbs_config = Signal(2)

        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)


        self.specials += [
            MultiReg(self.tx_produce_square_wave.storage, tx_produce_square_wave, "tx"),
            MultiReg(self.tx_prbs_config.storage, tx_prbs_config, "tx"),
        ]

        self.specials += [
            MultiReg(self.rx_prbs_config.storage, rx_prbs_config, "rx"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors.status, "sys"), # FIXME
        ]

        # # #

        # TX generates RTIO clock, init must be in system domain
        tx_init = GTXInit(sys_clk_freq, False)
        # RX receives restart commands from RTIO domain
        rx_init = ClockDomainsRenamer("tx")(
            GTXInit(self.tx_clk_freq, True))
        self.submodules += tx_init, rx_init
        self.comb += [
            tx_init.plllock.eq(cpll.lock),
            rx_init.plllock.eq(cpll.lock),
            cpll.reset.eq(tx_init.pllreset)
        ]

        assert cpll.config["linerate"] < 6.6e9
        rxcdr_cfgs = {
            1 : 0x03000023ff10400020,
            2 : 0x03000023ff10200020,
            4 : 0x03000023ff10100020,
            8 : 0x03000023ff10080020
        }

        txdata = Signal(20)
        rxdata = Signal(20)
        self.specials += \
            Instance("GTXE2_CHANNEL",
                # PMA Attributes
                p_PMA_RSV=0x00018480,
                p_PMA_RSV2=0x2050,
                p_PMA_RSV3=0,
                p_PMA_RSV4=0,
                p_RX_BIAS_CFG=0b100,
                p_RX_CM_TRIM=0b010,
                p_RX_OS_CFG=0b10000000,
                p_RX_CLK25_DIV=5,
                p_TX_CLK25_DIV=5,

                # Power-Down Attributes
                p_PD_TRANS_TIME_FROM_P2=0x3c,
                p_PD_TRANS_TIME_NONE_P2=0x3c,
                p_PD_TRANS_TIME_TO_P2=0x64,

                # CPLL
                p_CPLL_CFG=0xBC07DC,
                p_CPLL_FBDIV=cpll.config["n2"],
                p_CPLL_FBDIV_45=cpll.config["n1"],
                p_CPLL_REFCLK_DIV=cpll.config["m"],
                p_RXOUT_DIV=cpll.config["d"],
                p_TXOUT_DIV=cpll.config["d"],
                o_CPLLLOCK=cpll.lock,
                i_CPLLLOCKEN=1,
                i_CPLLREFCLKSEL=0b001,
                i_TSTIN=2**20-1,
                i_GTREFCLK0=cpll.refclk,

                # TX clock
                p_TXBUF_EN="FALSE",
                p_TX_XCLK_SEL="TXUSR",
                o_TXOUTCLK=self.txoutclk,
                i_TXSYSCLKSEL=0b00,
                i_TXOUTCLKSEL=0b11,

                # TX Startup/Reset
                i_GTTXRESET=tx_init.gtXxreset,
                o_TXRESETDONE=tx_init.Xxresetdone,
                i_TXDLYSRESET=tx_init.Xxdlysreset,
                o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
                o_TXPHALIGNDONE=tx_init.Xxphaligndone,
                i_TXUSERRDY=tx_init.Xxuserrdy,

                # TX data
                p_TX_DATA_WIDTH=20,
                p_TX_INT_DATAWIDTH=0,
                i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]),
                i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]),
                i_TXDATA=Cat(txdata[:8], txdata[10:18]),
                i_TXUSRCLK=ClockSignal("tx"),
                i_TXUSRCLK2=ClockSignal("tx"),

                # TX electrical
                i_TXBUFDIFFCTRL=0b100,
                i_TXDIFFCTRL=0b1000,

                # Internal Loopback
                i_LOOPBACK=0b010 if internal_loopback else 0b000,

                # RX Startup/Reset
                i_GTRXRESET=rx_init.gtXxreset,
                o_RXRESETDONE=rx_init.Xxresetdone,
                i_RXDLYSRESET=rx_init.Xxdlysreset,
                o_RXDLYSRESETDONE=rx_init.Xxdlysresetdone,
                o_RXPHALIGNDONE=rx_init.Xxphaligndone,
                i_RXUSERRDY=rx_init.Xxuserrdy,

                # RX AFE
                p_RX_DFE_XYD_CFG=0,
                # tests results @ 1.25Gbps:
                # (1) SFP 10GbE optical loopback
                # (2) SFP copper loopback
                # Xilinx's default value: 0x3008e56a
                #p_RX_DFE_KL_CFG2=0x3008e56a, # (1): errors+++, (2): errors+
                p_RX_DFE_KL_CFG2=0x3310180c, # (1): working, (2): not working
                #p_RX_DFE_KL_CFG2=0x3010d90c, # (1): errors+, (2): not working
                #p_RX_DFE_KL_CFG2=0x301148ac, # (1): errors+, (2): not working
                i_RXDFEXYDEN=1,
                i_RXDFEXYDHOLD=0,
                i_RXDFEXYDOVRDEN=0,
                i_RXLPMEN=0,

                # RX clock
                p_RXBUF_EN="FALSE",
                p_RX_XCLK_SEL="RXUSR",
                i_RXDDIEN=1,
                i_RXSYSCLKSEL=0b00,
                i_RXOUTCLKSEL=0b010,
                o_RXOUTCLK=self.rxoutclk,
                i_RXUSRCLK=ClockSignal("rx"),
                i_RXUSRCLK2=ClockSignal("rx"),
                p_RXCDR_CFG=rxcdr_cfgs[cpll.config["d"]],

                # RX Clock Correction Attributes
                p_CLK_CORRECT_USE="FALSE",
                p_CLK_COR_SEQ_1_1=0b0100000000,
                p_CLK_COR_SEQ_2_1=0b0100000000,
                p_CLK_COR_SEQ_1_ENABLE=0b1111,
                p_CLK_COR_SEQ_2_ENABLE=0b1111,

                # RX data
                p_RX_DATA_WIDTH=20,
                p_RX_INT_DATAWIDTH=0,
                o_RXDISPERR=Cat(rxdata[9], rxdata[19]),
                o_RXCHARISK=Cat(rxdata[8], rxdata[18]),
                o_RXDATA=Cat(rxdata[:8], rxdata[10:18]),

                # Polarity
                i_TXPOLARITY=tx_polarity,
                i_RXPOLARITY=rx_polarity,

                # Pads
                i_GTXRXP=rx_pads.p,
                i_GTXRXN=rx_pads.n,
                o_GTXTXP=tx_pads.p,
                o_GTXTXN=tx_pads.n,
            )

        # tx clocking
        tx_reset_deglitched = Signal()
        tx_reset_deglitched.attr.add("no_retiming")
        self.sync += tx_reset_deglitched.eq(~tx_init.done)
        self.clock_domains.cd_tx = ClockDomain()
        txoutclk_bufg = Signal()
        txoutclk_bufr = Signal()
        tx_bufr_div = cpll.config["clkin"]/self.tx_clk_freq
        assert tx_bufr_div == int(tx_bufr_div)
        self.specials += [
            Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_bufg),
            # TODO: use MMCM instead?
            Instance("BUFR", i_I=txoutclk_bufg, o_O=txoutclk_bufr,
                i_CE=1, p_BUFR_DIVIDE=str(int(tx_bufr_div))),
            Instance("BUFG", i_I=txoutclk_bufr, o_O=self.cd_tx.clk),
            AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
        ]

        # rx clocking
        rx_reset_deglitched = Signal()
        rx_reset_deglitched.attr.add("no_retiming")
        self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
        self.clock_domains.cd_rx = ClockDomain()
        self.specials += [
            Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
            AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
        ]

        # tx data and prbs
        self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(20, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.comb += [
            self.tx_prbs.i.eq(Cat(*[self.encoder.output[i] for i in range(2)])),
            If(tx_produce_square_wave,
                # square wave @ linerate/20 for scope observation
                txdata.eq(0b11111111110000000000)
            ).Else(
                txdata.eq(self.tx_prbs.o)
            )
        ]

        # rx data and prbs
        self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(20, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        self.comb += [
            self.decoders[0].input.eq(rxdata[:10]),
            self.decoders[1].input.eq(rxdata[10:]),
            self.rx_prbs.i.eq(rxdata)
        ]

        # clock alignment
        if clock_aligner:
            clock_aligner = BruteforceClockAligner(0b0101111100, self.tx_clk_freq)
            self.submodules += clock_aligner
            self.comb += [
                clock_aligner.rxdata.eq(rxdata),
                rx_init.restart.eq(clock_aligner.restart),
                self.rx_ready.eq(clock_aligner.ready)
            ]
        else:
            self.comb += self.rx_ready.eq(rx_init.done)
Example #5
0
    def __init__(self,
                 pll,
                 tx_pads,
                 rx_pads,
                 sys_clk_freq,
                 data_width=20,
                 tx_buffer_enable=False,
                 rx_buffer_enable=False,
                 clock_aligner=True,
                 clock_aligner_comma=0b0101111100,
                 tx_polarity=0,
                 rx_polarity=0):
        assert (data_width == 20) or (data_width == 40)

        # TX controls
        self.tx_enable = Signal()
        self.tx_ready = Signal()
        self.tx_inhibit = Signal()
        self.tx_produce_square_wave = Signal()
        self.tx_produce_pattern = Signal()
        self.tx_pattern = Signal(data_width)
        self.tx_prbs_config = Signal(2)

        # RX controls
        self.rx_enable = Signal()
        self.rx_ready = Signal()
        self.rx_align = Signal(reset=1)
        self.rx_prbs_config = Signal(2)
        self.rx_prbs_errors = Signal(32)

        # DRP
        self.drp = DRPInterface()

        # Loopback
        self.loopback = Signal(3)

        # # #

        use_cpll = isinstance(pll, GTHChannelPLL)
        use_qpll0 = isinstance(pll,
                               GTHQuadPLL) and pll.config["qpll"] == "qpll0"
        use_qpll1 = isinstance(pll,
                               GTHQuadPLL) and pll.config["qpll"] == "qpll1"

        self.nwords = nwords = data_width // 10

        self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(
            nwords, True))
        self.decoders = [
            ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(nwords)
        ]
        self.submodules += self.decoders

        # Transceiver direct clock outputs (useful to specify clock constraints)
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        self.tx_clk_freq = pll.config["linerate"] / data_width
        self.rx_clk_freq = pll.config["linerate"] / data_width

        # Control/Status CDC
        tx_produce_square_wave = Signal()
        tx_produce_pattern = Signal()
        tx_prbs_config = Signal(2)

        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        self.specials += [
            MultiReg(self.tx_produce_square_wave, tx_produce_square_wave,
                     "tx"),
            MultiReg(self.tx_produce_pattern, tx_produce_pattern, "tx"),
            MultiReg(self.tx_prbs_config, tx_prbs_config, "tx"),
        ]

        self.specials += [
            MultiReg(self.rx_prbs_config, rx_prbs_config, "rx"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors, "sys"),  # FIXME
        ]

        # # #

        # TX init ----------------------------------------------------------------------------------
        self.submodules.tx_init = tx_init = GTHTXInit(sys_clk_freq)
        self.comb += [
            self.tx_ready.eq(tx_init.done),
            tx_init.restart.eq(~self.tx_enable)
        ]

        # RX init ----------------------------------------------------------------------------------
        self.submodules.rx_init = rx_init = GTHRXInit(self.tx_clk_freq)
        self.comb += [
            self.rx_ready.eq(rx_init.done),
            rx_init.restart.eq(~self.rx_enable)
        ]

        # PLL ----------------------------------------------------------------------------------
        self.comb += [
            tx_init.plllock.eq(pll.lock),
            rx_init.plllock.eq(pll.lock),
            pll.reset.eq(tx_init.pllreset)
        ]

        # DRP mux ----------------------------------------------------------------------------------
        self.submodules.drp_mux = drp_mux = DRPMux()
        drp_mux.add_interface(self.drp)

        # GTHE3_CHANNEL instance -------------------------------------------------------------------
        txdata = Signal(data_width)
        rxdata = Signal(data_width)
        rxphaligndone = Signal()
        self.gth_params = dict(
            p_ACJTAG_DEBUG_MODE=0b0,
            p_ACJTAG_MODE=0b0,
            p_ACJTAG_RESET=0b0,
            p_ADAPT_CFG0=0b1111100000000000,
            p_ADAPT_CFG1=0b0000000000000000,
            p_ALIGN_COMMA_DOUBLE="FALSE",
            p_ALIGN_COMMA_ENABLE=0b0000000000,
            p_ALIGN_COMMA_WORD=1,
            p_ALIGN_MCOMMA_DET="FALSE",
            p_ALIGN_MCOMMA_VALUE=0b1010000011,
            p_ALIGN_PCOMMA_DET="FALSE",
            p_ALIGN_PCOMMA_VALUE=0b0101111100,
            p_A_RXOSCALRESET=0b0,
            p_A_RXPROGDIVRESET=0b0,
            p_A_TXPROGDIVRESET=0b0,
            p_CBCC_DATA_SOURCE_SEL="ENCODED",
            p_CDR_SWAP_MODE_EN=0b0,
            p_CHAN_BOND_KEEP_ALIGN="FALSE",
            p_CHAN_BOND_MAX_SKEW=1,
            p_CHAN_BOND_SEQ_1_1=0b0000000000,
            p_CHAN_BOND_SEQ_1_2=0b0000000000,
            p_CHAN_BOND_SEQ_1_3=0b0000000000,
            p_CHAN_BOND_SEQ_1_4=0b0000000000,
            p_CHAN_BOND_SEQ_1_ENABLE=0b1111,
            p_CHAN_BOND_SEQ_2_1=0b0000000000,
            p_CHAN_BOND_SEQ_2_2=0b0000000000,
            p_CHAN_BOND_SEQ_2_3=0b0000000000,
            p_CHAN_BOND_SEQ_2_4=0b0000000000,
            p_CHAN_BOND_SEQ_2_ENABLE=0b1111,
            p_CHAN_BOND_SEQ_2_USE="FALSE",
            p_CHAN_BOND_SEQ_LEN=1,
            p_CLK_CORRECT_USE="FALSE",
            p_CLK_COR_KEEP_IDLE="FALSE",
            p_CLK_COR_MAX_LAT=12 if rx_buffer_enable else 20,
            p_CLK_COR_MIN_LAT=8 if rx_buffer_enable else 18,
            p_CLK_COR_PRECEDENCE="TRUE",
            p_CLK_COR_REPEAT_WAIT=0,
            p_CLK_COR_SEQ_1_1=0b0000000000,
            p_CLK_COR_SEQ_1_2=0b0000000000,
            p_CLK_COR_SEQ_1_3=0b0000000000,
            p_CLK_COR_SEQ_1_4=0b0000000000,
            p_CLK_COR_SEQ_1_ENABLE=0b1111,
            p_CLK_COR_SEQ_2_1=0b0000000000,
            p_CLK_COR_SEQ_2_2=0b0000000000,
            p_CLK_COR_SEQ_2_3=0b0000000000,
            p_CLK_COR_SEQ_2_4=0b0000000000,
            p_CLK_COR_SEQ_2_ENABLE=0b1111,
            p_CLK_COR_SEQ_2_USE="FALSE",
            p_CLK_COR_SEQ_LEN=1,
            p_CPLL_CFG0=0b0110011111111000,
            p_CPLL_CFG1=0b1010010010101100,
            p_CPLL_CFG2=0b0000000000000111,
            p_CPLL_CFG3=0b000000,
            p_CPLL_FBDIV=1 if (use_qpll0 | use_qpll1) else pll.config["n2"],
            p_CPLL_FBDIV_45=4 if (use_qpll0 | use_qpll1) else pll.config["n1"],
            p_CPLL_INIT_CFG0=0b0000001010110010,
            p_CPLL_INIT_CFG1=0b00000000,
            p_CPLL_LOCK_CFG=0b0000000111101000,
            p_CPLL_REFCLK_DIV=1 if
            (use_qpll0 | use_qpll1) else pll.config["m"],
            p_DDI_CTRL=0b00,
            p_DDI_REALIGN_WAIT=15,
            p_DEC_MCOMMA_DETECT="FALSE",
            p_DEC_PCOMMA_DETECT="FALSE",
            p_DEC_VALID_COMMA_ONLY="FALSE",
            p_DFE_D_X_REL_POS=0b0,
            p_DFE_VCM_COMP_EN=0b0,
            p_DMONITOR_CFG0=0b0000000000,
            p_DMONITOR_CFG1=0b00000000,
            p_ES_CLK_PHASE_SEL=0b0,
            p_ES_CONTROL=0b000000,
            p_ES_ERRDET_EN="FALSE",
            p_ES_EYE_SCAN_EN="FALSE",
            p_ES_HORZ_OFFSET=0b000000000000,
            p_ES_PMA_CFG=0b0000000000,
            p_ES_PRESCALE=0b00000,
            p_ES_QUALIFIER0=0b0000000000000000,
            p_ES_QUALIFIER1=0b0000000000000000,
            p_ES_QUALIFIER2=0b0000000000000000,
            p_ES_QUALIFIER3=0b0000000000000000,
            p_ES_QUALIFIER4=0b0000000000000000,
            p_ES_QUAL_MASK0=0b0000000000000000,
            p_ES_QUAL_MASK1=0b0000000000000000,
            p_ES_QUAL_MASK2=0b0000000000000000,
            p_ES_QUAL_MASK3=0b0000000000000000,
            p_ES_QUAL_MASK4=0b0000000000000000,
            p_ES_SDATA_MASK0=0b0000000000000000,
            p_ES_SDATA_MASK1=0b0000000000000000,
            p_ES_SDATA_MASK2=0b0000000000000000,
            p_ES_SDATA_MASK3=0b0000000000000000,
            p_ES_SDATA_MASK4=0b0000000000000000,
            p_EVODD_PHI_CFG=0b00000000000,
            p_EYE_SCAN_SWAP_EN=0b0,
            p_FTS_DESKEW_SEQ_ENABLE=0b1111,
            p_FTS_LANE_DESKEW_CFG=0b1111,
            p_FTS_LANE_DESKEW_EN="FALSE",
            p_GEARBOX_MODE=0b00000,
            p_GM_BIAS_SELECT=0b0,
            p_LOCAL_MASTER=0b1,
            p_OOBDIVCTL=0b00,
            p_OOB_PWRUP=0b0,
            p_PCI3_AUTO_REALIGN="OVR_1K_BLK",
            p_PCI3_PIPE_RX_ELECIDLE=0b0,
            p_PCI3_RX_ASYNC_EBUF_BYPASS=0b00,
            p_PCI3_RX_ELECIDLE_EI2_ENABLE=0b0,
            p_PCI3_RX_ELECIDLE_H2L_COUNT=0b000000,
            p_PCI3_RX_ELECIDLE_H2L_DISABLE=0b000,
            p_PCI3_RX_ELECIDLE_HI_COUNT=0b000000,
            p_PCI3_RX_ELECIDLE_LP4_DISABLE=0b0,
            p_PCI3_RX_FIFO_DISABLE=0b0,
            p_PCIE_BUFG_DIV_CTRL=0b0001000000000000,
            p_PCIE_RXPCS_CFG_GEN3=0b0000001010100100,
            p_PCIE_RXPMA_CFG=0b0000000000001010,
            p_PCIE_TXPCS_CFG_GEN3=0b0010010010100100,
            p_PCIE_TXPMA_CFG=0b0000000000001010,
            p_PCS_PCIE_EN="FALSE",
            p_PCS_RSVD0=0b0000000000000000,
            p_PCS_RSVD1=0b000,
            p_PD_TRANS_TIME_FROM_P2=0b000000111100,
            p_PD_TRANS_TIME_NONE_P2=0b00011001,
            p_PD_TRANS_TIME_TO_P2=0b01100100,
            p_PLL_SEL_MODE_GEN12=0b00,
            p_PLL_SEL_MODE_GEN3=0b11,
            p_PMA_RSV1=0b1111000000000000,
            p_PROCESS_PAR=0b010,
            p_RATE_SW_USE_DRP=0b1,
            p_RESET_POWERSAVE_DISABLE=0b0,
        )
        self.gth_params.update(
            p_RXBUFRESET_TIME=0b00011,
            p_RXBUF_ADDR_MODE="FAST",
            p_RXBUF_EIDLE_HI_CNT=0b1000,
            p_RXBUF_EIDLE_LO_CNT=0b0000,
            p_RXBUF_EN="TRUE" if rx_buffer_enable else "FALSE",
            p_RXBUF_RESET_ON_CB_CHANGE="TRUE",
            p_RXBUF_RESET_ON_COMMAALIGN="FALSE",
            p_RXBUF_RESET_ON_EIDLE="FALSE",
            p_RXBUF_RESET_ON_RATE_CHANGE="TRUE",
            p_RXBUF_THRESH_OVFLW=57 if rx_buffer_enable else 0,
            p_RXBUF_THRESH_OVRD="TRUE" if rx_buffer_enable else "FALSE",
            p_RXBUF_THRESH_UNDFLW=3 if rx_buffer_enable else 0,
            p_RXCDRFREQRESET_TIME=0b00001,
            p_RXCDRPHRESET_TIME=0b00001,
            p_RXCDR_CFG0=0b0000000000000000,
            p_RXCDR_CFG0_GEN3=0b0000000000000000,
            p_RXCDR_CFG1=0b0000000000000000,
            p_RXCDR_CFG1_GEN3=0b0000000000000000,
            p_RXCDR_CFG2=0b0000011111010110,
            p_RXCDR_CFG2_GEN3=0b0000011111100110,
            p_RXCDR_CFG3=0b0000000000000000,
            p_RXCDR_CFG3_GEN3=0b0000000000000000,
            p_RXCDR_CFG4=0b0000000000000000,
            p_RXCDR_CFG4_GEN3=0b0000000000000000,
            p_RXCDR_CFG5=0b0000000000000000,
            p_RXCDR_CFG5_GEN3=0b0000000000000000,
            p_RXCDR_FR_RESET_ON_EIDLE=0b0,
            p_RXCDR_HOLD_DURING_EIDLE=0b0,
            p_RXCDR_LOCK_CFG0=0b0100010010000000,
            p_RXCDR_LOCK_CFG1=0b0101111111111111,
            p_RXCDR_LOCK_CFG2=0b0111011111000011,
            p_RXCDR_PH_RESET_ON_EIDLE=0b0,
            p_RXCFOK_CFG0=0b0100000000000000,
            p_RXCFOK_CFG1=0b0000000001100101,
            p_RXCFOK_CFG2=0b0000000000101110,
            p_RXDFELPMRESET_TIME=0b0001111,
            p_RXDFELPM_KL_CFG0=0b0000000000000000,
            p_RXDFELPM_KL_CFG1=0b0000000000000010,
            p_RXDFELPM_KL_CFG2=0b0000000000000000,
            p_RXDFE_CFG0=0b0000101000000000,
            p_RXDFE_CFG1=0b0000000000000000,
            p_RXDFE_GC_CFG0=0b0000000000000000,
            p_RXDFE_GC_CFG1=0b0111100001110000,
            p_RXDFE_GC_CFG2=0b0000000000000000,
            p_RXDFE_H2_CFG0=0b0000000000000000,
            p_RXDFE_H2_CFG1=0b0000000000000000,
            p_RXDFE_H3_CFG0=0b0100000000000000,
            p_RXDFE_H3_CFG1=0b0000000000000000,
            p_RXDFE_H4_CFG0=0b0010000000000000,
            p_RXDFE_H4_CFG1=0b0000000000000011,
            p_RXDFE_H5_CFG0=0b0010000000000000,
            p_RXDFE_H5_CFG1=0b0000000000000011,
            p_RXDFE_H6_CFG0=0b0010000000000000,
            p_RXDFE_H6_CFG1=0b0000000000000000,
            p_RXDFE_H7_CFG0=0b0010000000000000,
            p_RXDFE_H7_CFG1=0b0000000000000000,
            p_RXDFE_H8_CFG0=0b0010000000000000,
            p_RXDFE_H8_CFG1=0b0000000000000000,
            p_RXDFE_H9_CFG0=0b0010000000000000,
            p_RXDFE_H9_CFG1=0b0000000000000000,
            p_RXDFE_HA_CFG0=0b0010000000000000,
            p_RXDFE_HA_CFG1=0b0000000000000000,
            p_RXDFE_HB_CFG0=0b0010000000000000,
            p_RXDFE_HB_CFG1=0b0000000000000000,
            p_RXDFE_HC_CFG0=0b0000000000000000,
            p_RXDFE_HC_CFG1=0b0000000000000000,
            p_RXDFE_HD_CFG0=0b0000000000000000,
            p_RXDFE_HD_CFG1=0b0000000000000000,
            p_RXDFE_HE_CFG0=0b0000000000000000,
            p_RXDFE_HE_CFG1=0b0000000000000000,
            p_RXDFE_HF_CFG0=0b0000000000000000,
            p_RXDFE_HF_CFG1=0b0000000000000000,
            p_RXDFE_OS_CFG0=0b1000000000000000,
            p_RXDFE_OS_CFG1=0b0000000000000000,
            p_RXDFE_UT_CFG0=0b1000000000000000,
            p_RXDFE_UT_CFG1=0b0000000000000011,
            p_RXDFE_VP_CFG0=0b1010101000000000,
            p_RXDFE_VP_CFG1=0b0000000000110011,
            p_RXDLY_CFG=0b0000000000011111,
            p_RXDLY_LCFG=0b0000000000110000,
            p_RXELECIDLE_CFG="SIGCFG_4",
            p_RXGBOX_FIFO_INIT_RD_ADDR=4,
            p_RXGEARBOX_EN="FALSE",
            p_RXISCANRESET_TIME=0b00001,
            p_RXLPM_CFG=0b0000000000000000,
            p_RXLPM_GC_CFG=0b0001000000000000,
            p_RXLPM_KH_CFG0=0b0000000000000000,
            p_RXLPM_KH_CFG1=0b0000000000000010,
            p_RXLPM_OS_CFG0=0b1000000000000000,
            p_RXLPM_OS_CFG1=0b0000000000000010,
            p_RXOOB_CFG=0b000000110,
            p_RXOOB_CLK_CFG="PMA",
            p_RXOSCALRESET_TIME=0b00011,
            p_RXOUT_DIV=pll.config["d"],
            p_RXPCSRESET_TIME=0b00011,
            p_RXPHBEACON_CFG=0b0000000000000000,
            p_RXPHDLY_CFG=0b0010000000100000,
            p_RXPHSAMP_CFG=0b0010000100000000,
            p_RXPHSLIP_CFG=0b0110011000100010,
            p_RXPH_MONITOR_SEL=0b00000,
            p_RXPI_CFG0=0b00,
            p_RXPI_CFG1=0b00,
            p_RXPI_CFG2=0b00,
            p_RXPI_CFG3=0b00,
            p_RXPI_CFG4=0b1,
            p_RXPI_CFG5=0b1,
            p_RXPI_CFG6=0b000,
            p_RXPI_LPM=0b0,
            p_RXPI_VREFSEL=0b0,
            p_RXPMACLK_SEL="DATA",
            p_RXPMARESET_TIME=0b00011,
            p_RXPRBS_ERR_LOOPBACK=0b0,
            p_RXPRBS_LINKACQ_CNT=15,
            p_RXSLIDE_AUTO_WAIT=7,
            p_RXSLIDE_MODE="OFF",
            p_RXSYNC_MULTILANE=0b0,
            p_RXSYNC_OVRD=0b0,
            p_RXSYNC_SKIP_DA=0b0,
            p_RX_AFE_CM_EN=0b0,
            p_RX_BIAS_CFG0=0b0000101010110100,
            p_RX_BUFFER_CFG=0b000000,
            p_RX_CAPFF_SARC_ENB=0b0,
            p_RX_CLK25_DIV=5,
            p_RX_CLKMUX_EN=0b1,
            p_RX_CLK_SLIP_OVRD=0b00000,
            p_RX_CM_BUF_CFG=0b1010,
            p_RX_CM_BUF_PD=0b0,
            p_RX_CM_SEL=0b11,
            p_RX_CM_TRIM=0b1010,
            p_RX_CTLE3_LPF=0b00000001,
            p_RX_DATA_WIDTH=data_width,
            p_RX_DDI_SEL=0b000000,
            p_RX_DEFER_RESET_BUF_EN="TRUE",
            p_RX_DFELPM_CFG0=0b0110,
            p_RX_DFELPM_CFG1=0b1,
            p_RX_DFELPM_KLKH_AGC_STUP_EN=0b1,
            p_RX_DFE_AGC_CFG0=0b10,
            p_RX_DFE_AGC_CFG1=0b100,
            p_RX_DFE_KL_LPM_KH_CFG0=0b01,
            p_RX_DFE_KL_LPM_KH_CFG1=0b100,
            p_RX_DFE_KL_LPM_KL_CFG0=0b01,
            p_RX_DFE_KL_LPM_KL_CFG1=0b100,
            p_RX_DFE_LPM_HOLD_DURING_EIDLE=0b0,
            p_RX_DISPERR_SEQ_MATCH="TRUE",
            p_RX_DIVRESET_TIME=0b00001,
            p_RX_EN_HI_LR=0b0,
            p_RX_EYESCAN_VS_CODE=0b0000000,
            p_RX_EYESCAN_VS_NEG_DIR=0b0,
            p_RX_EYESCAN_VS_RANGE=0b00,
            p_RX_EYESCAN_VS_UT_SIGN=0b0,
            p_RX_FABINT_USRCLK_FLOP=0b0,
            p_RX_INT_DATAWIDTH=data_width == 40,
            p_RX_PMA_POWER_SAVE=0b0,
            p_RX_PROGDIV_CFG=0.0,
            p_RX_SAMPLE_PERIOD=0b111,
            p_RX_SIG_VALID_DLY=11,
            p_RX_SUM_DFETAPREP_EN=0b0,
            p_RX_SUM_IREF_TUNE=0b0000,
            p_RX_SUM_RES_CTRL=0b00,
            p_RX_SUM_VCMTUNE=0b0000,
            p_RX_SUM_VCM_OVWR=0b0,
            p_RX_SUM_VREF_TUNE=0b000,
            p_RX_TUNE_AFE_OS=0b10,
            p_RX_WIDEMODE_CDR=0b0,
            p_RX_XCLK_SEL="RXDES" if rx_buffer_enable else "RXUSR",
            p_SAS_MAX_COM=64,
            p_SAS_MIN_COM=36,
            p_SATA_BURST_SEQ_LEN=0b1110,
            p_SATA_CPLL_CFG="VCO_3000MHZ",
            p_SATA_MAX_BURST=8,
            p_SATA_MAX_INIT=21,
            p_SATA_MAX_WAKE=7,
            p_SATA_MIN_BURST=4,
            p_SATA_MIN_INIT=12,
            p_SATA_MIN_WAKE=4,
            p_SHOW_REALIGN_COMMA="TRUE",
            p_SIM_RECEIVER_DETECT_PASS="******",
            p_SIM_RESET_SPEEDUP="TRUE",
            p_SIM_TX_EIDLE_DRIVE_LEVEL=0b0,
            p_SIM_VERSION=2,
            p_TAPDLY_SET_TX=0b00,
            p_TEMPERATUR_PAR=0b0010,
            p_TERM_RCAL_CFG=0b100001000010000,
            p_TERM_RCAL_OVRD=0b000,
            p_TRANS_TIME_RATE=0b00001110,
            p_TST_RSV0=0b00000000,
            p_TST_RSV1=0b00000000,
        )
        self.gth_params.update(
            p_TXBUF_EN="TRUE" if tx_buffer_enable else "FALSE",
            p_TXBUF_RESET_ON_RATE_CHANGE="TRUE",
            p_TXDLY_CFG=0b0000000000001001,
            p_TXDLY_LCFG=0b0000000001010000,
            p_TXDRVBIAS_N=0b1010,
            p_TXDRVBIAS_P=0b1010,
            p_TXFIFO_ADDR_CFG="LOW",
            p_TXGBOX_FIFO_INIT_RD_ADDR=4,
            p_TXGEARBOX_EN="FALSE",
            p_TXOUT_DIV=pll.config["d"],
            p_TXPCSRESET_TIME=0b00011,
            p_TXPHDLY_CFG0=0b0010000000100000,
            p_TXPHDLY_CFG1=0b0000000001110101,
            p_TXPH_CFG=0b0000100110000000,
            p_TXPH_MONITOR_SEL=0b00000,
            p_TXPI_CFG0=0b00,
            p_TXPI_CFG1=0b00,
            p_TXPI_CFG2=0b00,
            p_TXPI_CFG3=0b1,
            p_TXPI_CFG4=0b1,
            p_TXPI_CFG5=0b000,
            p_TXPI_GRAY_SEL=0b0,
            p_TXPI_INVSTROBE_SEL=0b0,
            p_TXPI_LPM=0b0,
            p_TXPI_PPMCLK_SEL="TXUSRCLK2",
            p_TXPI_PPM_CFG=0b00000000,
            p_TXPI_SYNFREQ_PPM=0b001,
            p_TXPI_VREFSEL=0b0,
            p_TXPMARESET_TIME=0b00011,
            p_TXSYNC_MULTILANE=0,
            p_TXSYNC_OVRD=0b0,
            p_TXSYNC_SKIP_DA=0b0,
            p_TX_CLK25_DIV=5,
            p_TX_CLKMUX_EN=0b1,
            p_TX_DATA_WIDTH=data_width,
            p_TX_DCD_CFG=0b000010,
            p_TX_DCD_EN=0b0,
            p_TX_DEEMPH0=0b000000,
            p_TX_DEEMPH1=0b000000,
            p_TX_DIVRESET_TIME=0b00001,
            p_TX_DRIVE_MODE="DIRECT",
            p_TX_EIDLE_ASSERT_DELAY=0b100,
            p_TX_EIDLE_DEASSERT_DELAY=0b011,
            p_TX_EML_PHI_TUNE=0b0,
            p_TX_FABINT_USRCLK_FLOP=0b0,
            p_TX_IDLE_DATA_ZERO=0b0,
            p_TX_INT_DATAWIDTH=data_width == 40,
            p_TX_LOOPBACK_DRIVE_HIZ="FALSE",
            p_TX_MAINCURSOR_SEL=0b0,
            p_TX_MARGIN_FULL_0=0b1001111,
            p_TX_MARGIN_FULL_1=0b1001110,
            p_TX_MARGIN_FULL_2=0b1001100,
            p_TX_MARGIN_FULL_3=0b1001010,
            p_TX_MARGIN_FULL_4=0b1001000,
            p_TX_MARGIN_LOW_0=0b1000110,
            p_TX_MARGIN_LOW_1=0b1000101,
            p_TX_MARGIN_LOW_2=0b1000011,
            p_TX_MARGIN_LOW_3=0b1000010,
            p_TX_MARGIN_LOW_4=0b1000000,
            p_TX_MODE_SEL=0b000,
            p_TX_PMADATA_OPT=0b0,
            p_TX_PMA_POWER_SAVE=0b0,
            p_TX_PROGCLK_SEL="PREPI",
            p_TX_PROGDIV_CFG=0.0,
            p_TX_QPI_STATUS_EN=0b0,
            p_TX_RXDETECT_CFG=0b00000000110010,
            p_TX_RXDETECT_REF=0b100,
            p_TX_SAMPLE_PERIOD=0b111,
            p_TX_SARC_LPBK_ENB=0b0,
            p_TX_XCLK_SEL="TXOUT" if tx_buffer_enable else "TXUSR",
            p_USE_PCS_CLK_PHASE_SEL=0b0,
            p_WB_MODE=0b00,
        )
        self.gth_params.update(
            # Reset modes
            i_GTRESETSEL=0,
            i_RESETOVRD=0,

            # DRP
            i_DRPADDR=drp_mux.addr,
            i_DRPCLK=drp_mux.clk,
            i_DRPDI=drp_mux.di,
            o_DRPDO=drp_mux.do,
            i_DRPEN=drp_mux.en,
            o_DRPRDY=drp_mux.rdy,
            i_DRPWE=drp_mux.we,

            # CPLL
            i_CPLLRESET=0,
            i_CPLLPD=0 if (use_qpll0 | use_qpll1) else pll.reset,
            o_CPLLLOCK=Signal() if (use_qpll0 | use_qpll1) else pll.lock,
            i_CPLLLOCKEN=1,
            i_CPLLREFCLKSEL=0b001,
            i_TSTIN=2**20 - 1,
            i_GTREFCLK0=0 if (use_qpll0 | use_qpll1) else pll.refclk,

            # QPLL
            i_QPLL0CLK=0 if (use_cpll | use_qpll1) else pll.clk,
            i_QPLL0REFCLK=0 if (use_cpll | use_qpll1) else pll.refclk,
            i_QPLL1CLK=0 if (use_cpll | use_qpll0) else pll.clk,
            i_QPLL1REFCLK=0 if (use_cpll | use_qpll0) else pll.refclk,

            # TX clock
            o_TXOUTCLK=self.txoutclk,
            i_TXSYSCLKSEL=0b00 if use_cpll else 0b10 if use_qpll0 else 0b11,
            i_TXPLLCLKSEL=0b00 if use_cpll else 0b11 if use_qpll0 else 0b10,
            i_TXOUTCLKSEL=0b010 if tx_buffer_enable else 0b011,

            # TX Startup/Reset
            i_GTTXRESET=tx_init.gtXxreset,
            o_TXRESETDONE=tx_init.Xxresetdone,
            i_TXDLYSRESET=tx_init.Xxdlysreset,
            o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
            o_TXPHALIGNDONE=tx_init.Xxphaligndone,
            i_TXUSERRDY=tx_init.Xxuserrdy,
            i_TXSYNCMODE=1,
            i_TXDLYBYPASS=1 if tx_buffer_enable else 0,
            i_TXPHDLYPD=1 if tx_buffer_enable else 0,

            # TX data
            i_TXCTRL0=Cat(*[txdata[10 * i + 8] for i in range(nwords)]),
            i_TXCTRL1=Cat(*[txdata[10 * i + 9] for i in range(nwords)]),
            i_TXDATA=Cat(*[txdata[10 * i:10 * i + 8] for i in range(nwords)]),
            i_TXUSRCLK=ClockSignal("tx"),
            i_TXUSRCLK2=ClockSignal("tx"),

            # TX electrical
            i_TXPD=0b00,
            i_TXBUFDIFFCTRL=0b000,
            i_TXDIFFCTRL=0b1100,
            i_TXINHIBIT=self.tx_inhibit,

            # Internal Loopback
            i_LOOPBACK=self.loopback,

            # RX Startup/Reset
            i_GTRXRESET=rx_init.gtXxreset,
            o_RXRESETDONE=rx_init.Xxresetdone,
            i_RXDLYSRESET=rx_init.Xxdlysreset,
            o_RXPHALIGNDONE=rxphaligndone,
            i_RXSYNCALLIN=rxphaligndone,
            i_RXUSERRDY=rx_init.Xxuserrdy,
            i_RXSYNCIN=0,
            i_RXSYNCMODE=1,
            o_RXSYNCDONE=rx_init.Xxsyncdone,
            i_RXDLYBYPASS=1 if rx_buffer_enable else 0,
            i_RXPHDLYPD=1 if rx_buffer_enable else 0,

            # RX AFE
            i_RXDFEAGCCTRL=1,
            i_RXDFEXYDEN=1,
            i_RXLPMEN=1,
            i_RXOSINTCFG=0xd,
            i_RXOSINTEN=1,

            # RX clock
            i_RXRATE=0,
            i_RXSYSCLKSEL=0b00,
            i_RXOUTCLKSEL=0b010,
            i_RXPLLCLKSEL=0b00,
            o_RXOUTCLK=self.rxoutclk,
            i_RXUSRCLK=ClockSignal("rx"),
            i_RXUSRCLK2=ClockSignal("rx"),

            # RX data
            o_RXCTRL0=Cat(*[rxdata[10 * i + 8] for i in range(nwords)]),
            o_RXCTRL1=Cat(*[rxdata[10 * i + 9] for i in range(nwords)]),
            o_RXDATA=Cat(*[rxdata[10 * i:10 * i + 8] for i in range(nwords)]),

            # RX electrical
            i_RXPD=0b00,
            i_RXELECIDLEMODE=0b11,

            # Polarity
            i_TXPOLARITY=tx_polarity,
            i_RXPOLARITY=rx_polarity,

            # Pads
            i_GTHRXP=rx_pads.p,
            i_GTHRXN=rx_pads.n,
            o_GTHTXP=tx_pads.p,
            o_GTHTXN=tx_pads.n)

        # TX clocking ------------------------------------------------------------------------------
        tx_reset_deglitched = Signal()
        tx_reset_deglitched.attr.add("no_retiming")
        self.sync += tx_reset_deglitched.eq(~tx_init.done)
        self.clock_domains.cd_tx = ClockDomain()
        if not tx_buffer_enable:
            tx_bufg_div = pll.config["clkin"] / self.tx_clk_freq
        else:
            txoutclk_div = 1
        assert tx_bufg_div == int(tx_bufg_div)
        self.specials += [
            Instance("BUFG_GT",
                     i_I=self.txoutclk,
                     o_O=self.cd_tx.clk,
                     i_DIV=int(tx_bufg_div) - 1),
            AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
        ]

        # RX clocking ------------------------------------------------------------------------------
        rx_reset_deglitched = Signal()
        rx_reset_deglitched.attr.add("no_retiming")
        self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
        self.clock_domains.cd_rx = ClockDomain()
        self.specials += [
            Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
            AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
        ]

        # TX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(
            data_width, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.comb += [
            self.tx_prbs.i.eq(
                Cat(*[self.encoder.output[i] for i in range(nwords)])),
            If(
                tx_produce_square_wave,
                # square wave @ linerate/data_width for scope observation
                txdata.eq(
                    Signal(data_width, reset=(1 << (data_width // 2)) - 1)
                )).Elif(tx_produce_pattern, txdata.eq(self.tx_pattern)).Else(
                    txdata.eq(self.tx_prbs.o))
        ]

        # RX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(
            data_width, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        for i in range(nwords):
            self.comb += self.decoders[i].input.eq(rxdata[10 * i:10 * (i + 1)])
        self.comb += self.rx_prbs.i.eq(rxdata)

        # Clock Aligner ----------------------------------------------------------------------------
        if clock_aligner:
            clock_aligner = BruteforceClockAligner(clock_aligner_comma,
                                                   self.tx_clk_freq)
            self.submodules.clock_aligner = clock_aligner
            ps_restart = PulseSynchronizer("tx", "sys")
            self.submodules += ps_restart
            self.comb += [
                clock_aligner.rxdata.eq(rxdata),
                ps_restart.i.eq(clock_aligner.restart),
                rx_init.restart.eq((ps_restart.o & self.rx_align)
                                   | ~self.rx_enable),
                self.rx_ready.eq(clock_aligner.ready)
            ]
Example #6
0
    def __init__(self, qpll, tx_pads, rx_pads, sys_clk_freq, data_width=20,
                 tx_buffer_enable=False, rx_buffer_enable=False,
                 clock_aligner=True, clock_aligner_comma=0b0101111100,
                 tx_polarity=0, rx_polarity=0):
        assert (data_width == 20)

        # TX controls
        self.tx_enable              = Signal()
        self.tx_ready               = Signal()
        self.tx_inhibit             = Signal()
        self.tx_produce_square_wave = Signal()
        self.tx_produce_pattern     = Signal()
        self.tx_pattern             = Signal(data_width)
        self.tx_prbs_config         = Signal(2)

        # RX controls
        self.rx_enable      = Signal()
        self.rx_ready       = Signal()
        self.rx_align       = Signal(reset=1)
        self.rx_prbs_config = Signal(2)
        self.rx_prbs_errors = Signal(32)

        # DRP
        self.drp = DRPInterface()

        # Loopback
        self.loopback = Signal(3)

        # # #

        self.nwords = nwords = data_width//10

        self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(nwords, True))
        self.decoders = [ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(nwords)]
        self.submodules += self.decoders

        # Transceiver direct clock outputs (useful to specify clock constraints)
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        self.tx_clk_freq = qpll.config["linerate"]/data_width
        self.rx_clk_freq = qpll.config["linerate"]/data_width

        # Control/Status CDC
        tx_produce_square_wave = Signal()
        tx_produce_pattern     = Signal()
        tx_prbs_config         = Signal(2)

        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        self.specials += [
            MultiReg(self.tx_produce_square_wave, tx_produce_square_wave, "tx"),
            MultiReg(self.tx_produce_pattern, tx_produce_pattern, "tx"),
            MultiReg(self.tx_prbs_config, tx_prbs_config, "tx"),
        ]

        self.specials += [
            MultiReg(self.rx_prbs_config, rx_prbs_config, "rx"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors, "sys"), # FIXME
        ]

        # # #

        assert qpll.config["linerate"] < 6.6e9
        rxcdr_cfgs = {
            1 : 0x0000107FE406001041010,
            2 : 0x0000107FE206001041010,
            4 : 0x0000107FE106001041010,
            8 : 0x0000107FE086001041010,
           16 : 0x0000107FE086001041010,
        }

        # TX init ----------------------------------------------------------------------------------
        self.submodules.tx_init = tx_init = GTPTXInit(sys_clk_freq, buffer_enable=tx_buffer_enable)
        self.comb += [
            self.tx_ready.eq(tx_init.done),
            tx_init.restart.eq(~self.tx_enable)
        ]

        # RX init ----------------------------------------------------------------------------------
        self.submodules.rx_init = rx_init = GTPRXInit(sys_clk_freq, buffer_enable=rx_buffer_enable)
        self.comb += [
            self.rx_ready.eq(rx_init.done),
            rx_init.restart.eq(~self.rx_enable)
        ]

        # PLL ----------------------------------------------------------------------------------
        self.comb += [
            tx_init.plllock.eq(qpll.lock),
            rx_init.plllock.eq(qpll.lock),
            qpll.reset.eq(tx_init.pllreset)
        ]

        # DRP mux ----------------------------------------------------------------------------------
        self.submodules.drp_mux = drp_mux = DRPMux()
        drp_mux.add_interface(rx_init.drp)
        drp_mux.add_interface(self.drp)

        # GTPE2_CHANNEL instance -------------------------------------------------------------------
        txdata = Signal(data_width)
        rxdata = Signal(data_width)
        rxphaligndone = Signal()

        self.gtp_params = dict(
            # Simulation-Only Attributes
            p_SIM_RECEIVER_DETECT_PASS   = "******",
            p_SIM_TX_EIDLE_DRIVE_LEVEL   = "X",
            p_SIM_RESET_SPEEDUP          = "FALSE",
            p_SIM_VERSION                = "2.0",

            # RX Byte and Word Alignment Attributes
            p_ALIGN_COMMA_DOUBLE         = "FALSE",
            p_ALIGN_COMMA_ENABLE         = 0b1111111111,
            p_ALIGN_COMMA_WORD           = 2 if data_width == 20 else 4,
            p_ALIGN_MCOMMA_DET           = "TRUE",
            p_ALIGN_MCOMMA_VALUE         = 0b1010000011,
            p_ALIGN_PCOMMA_DET           = "TRUE",
            p_ALIGN_PCOMMA_VALUE         = 0b0101111100,
            p_SHOW_REALIGN_COMMA         = "TRUE",
            p_RXSLIDE_AUTO_WAIT          = 7,
            p_RXSLIDE_MODE               = "OFF" if rx_buffer_enable else "PCS",
            p_RX_SIG_VALID_DLY           = 10,

            # RX 8B/10B Decoder Attributes
            p_RX_DISPERR_SEQ_MATCH       = "TRUE",
            p_DEC_MCOMMA_DETECT          = "TRUE",
            p_DEC_PCOMMA_DETECT          = "TRUE",
            p_DEC_VALID_COMMA_ONLY       = "TRUE",

            # RX Clock Correction Attributes
            p_CBCC_DATA_SOURCE_SEL       = "DECODED",
            p_CLK_COR_SEQ_2_USE          = "FALSE",
            p_CLK_COR_KEEP_IDLE          = "FALSE",
            p_CLK_COR_MAX_LAT            = 10 if data_width == 20 else 19,
            p_CLK_COR_MIN_LAT            = 8 if data_width == 20 else 15,
            p_CLK_COR_PRECEDENCE         = "TRUE",
            p_CLK_COR_REPEAT_WAIT        = 0,
            p_CLK_COR_SEQ_LEN            = 1,
            p_CLK_COR_SEQ_1_ENABLE       = 0b1111,
            p_CLK_COR_SEQ_1_1            = 0b0100000000,
            p_CLK_COR_SEQ_1_2            = 0b0000000000,
            p_CLK_COR_SEQ_1_3            = 0b0000000000,
            p_CLK_COR_SEQ_1_4            = 0b0000000000,
            p_CLK_CORRECT_USE            = "FALSE",
            p_CLK_COR_SEQ_2_ENABLE       = 0b1111,
            p_CLK_COR_SEQ_2_1            = 0b0100000000,
            p_CLK_COR_SEQ_2_2            = 0b0000000000,
            p_CLK_COR_SEQ_2_3            = 0b0000000000,
            p_CLK_COR_SEQ_2_4            = 0b0000000000,

            # RX Channel Bonding Attributes
            p_CHAN_BOND_KEEP_ALIGN       = "FALSE",
            p_CHAN_BOND_MAX_SKEW         = 1,
            p_CHAN_BOND_SEQ_LEN          = 1,
            p_CHAN_BOND_SEQ_1_1          = 0b0000000000,
            p_CHAN_BOND_SEQ_1_2          = 0b0000000000,
            p_CHAN_BOND_SEQ_1_3          = 0b0000000000,
            p_CHAN_BOND_SEQ_1_4          = 0b0000000000,
            p_CHAN_BOND_SEQ_1_ENABLE     = 0b1111,
            p_CHAN_BOND_SEQ_2_1          = 0b0000000000,
            p_CHAN_BOND_SEQ_2_2          = 0b0000000000,
            p_CHAN_BOND_SEQ_2_3          = 0b0000000000,
            p_CHAN_BOND_SEQ_2_4          = 0b0000000000,
            p_CHAN_BOND_SEQ_2_ENABLE     = 0b1111,
            p_CHAN_BOND_SEQ_2_USE        = "FALSE",
            p_FTS_DESKEW_SEQ_ENABLE      = 0b1111,
            p_FTS_LANE_DESKEW_CFG        = 0b1111,
            p_FTS_LANE_DESKEW_EN         = "FALSE",

            # RX Margin Analysis Attributes
            p_ES_CONTROL                 = 0b000000,
            p_ES_ERRDET_EN               = "FALSE",
            p_ES_EYE_SCAN_EN             = "TRUE",
            p_ES_HORZ_OFFSET             = 0x000,
            p_ES_PMA_CFG                 = 0b0000000000,
            p_ES_PRESCALE                = 0b00000,
            p_ES_QUALIFIER               = 0x00000000000000000000,
            p_ES_QUAL_MASK               = 0x00000000000000000000,
            p_ES_SDATA_MASK              = 0x00000000000000000000,
            p_ES_VERT_OFFSET             = 0b000000000,

            # FPGA RX Interface Attributes
            p_RX_DATA_WIDTH              = data_width,

            # PMA Attributes
            p_OUTREFCLK_SEL_INV          = 0b11,
            p_PMA_RSV                    = 0x00000333,
            p_PMA_RSV2                   = 0x00002040,
            p_PMA_RSV3                   = 0b00,
            p_PMA_RSV4                   = 0b0000,
            p_RX_BIAS_CFG                = 0b0000111100110011,
            p_DMONITOR_CFG               = 0x000A00,
            p_RX_CM_SEL                  = 0b01,
            p_RX_CM_TRIM                 = 0b0000,
            p_RX_DEBUG_CFG               = 0b00000000000000,
            p_RX_OS_CFG                  = 0b0000010000000,
            p_TERM_RCAL_CFG              = 0b100001000010000,
            p_TERM_RCAL_OVRD             = 0b000,
            p_TST_RSV                    = 0x00000000,
            p_RX_CLK25_DIV               = 5,
            p_TX_CLK25_DIV               = 5,
            p_UCODEER_CLR                = 0b0,

            # PCI Express Attributes
            p_PCS_PCIE_EN                = "FALSE",

            # PCS Attributes
            p_PCS_RSVD_ATTR              = 0x000000000000,

            # RX Buffer Attributes
            p_RXBUF_ADDR_MODE            = "FAST",
            p_RXBUF_EIDLE_HI_CNT         = 0b1000,
            p_RXBUF_EIDLE_LO_CNT         = 0b0000,
            p_RXBUF_EN                   = "TRUE" if rx_buffer_enable else "FALSE",
            p_RX_BUFFER_CFG              = 0b000000,
            p_RXBUF_RESET_ON_CB_CHANGE   = "TRUE",
            p_RXBUF_RESET_ON_COMMAALIGN  = "FALSE",
            p_RXBUF_RESET_ON_EIDLE       = "FALSE",
            p_RXBUF_RESET_ON_RATE_CHANGE = "TRUE",
            p_RXBUFRESET_TIME            = 0b00001,
            p_RXBUF_THRESH_OVFLW         = 61,
            p_RXBUF_THRESH_OVRD          = "FALSE",
            p_RXBUF_THRESH_UNDFLW        = 4,
            p_RXDLY_CFG                  = 0x001F,
            p_RXDLY_LCFG                 = 0x030,
            p_RXDLY_TAP_CFG              = 0x0000,
            p_RXPH_CFG                   = 0xC00002,
            p_RXPHDLY_CFG                = 0x084020,
            p_RXPH_MONITOR_SEL           = 0b00000,
            p_RX_XCLK_SEL                = "RXREC" if rx_buffer_enable else "RXUSR",
            p_RX_DDI_SEL                 = 0b000000,
            p_RX_DEFER_RESET_BUF_EN      = "TRUE",

            # CDR Attributes
            p_RXCDR_CFG                  = rxcdr_cfgs[qpll.config["d"]],
            p_RXCDR_FR_RESET_ON_EIDLE    = 0b0,
            p_RXCDR_HOLD_DURING_EIDLE    = 0b0,
            p_RXCDR_PH_RESET_ON_EIDLE    = 0b0,
            p_RXCDR_LOCK_CFG             = 0b001001,

            # RX Initialization and Reset Attributes
            p_RXCDRFREQRESET_TIME        = 0b00001,
            p_RXCDRPHRESET_TIME          = 0b00001,
            p_RXISCANRESET_TIME          = 0b00001,
            p_RXPCSRESET_TIME            = 0b00001,
            p_RXPMARESET_TIME            = 0b00011,

            # RX OOB Signaling Attributes
            p_RXOOB_CFG                  = 0b0000110,

            # RX Gearbox Attributes
            p_RXGEARBOX_EN               = "FALSE",
            p_GEARBOX_MODE               = 0b000,

            # PRBS Detection Attribute
            p_RXPRBS_ERR_LOOPBACK        = 0b0,

            # Power-Down Attributes
            p_PD_TRANS_TIME_FROM_P2      = 0x03c,
            p_PD_TRANS_TIME_NONE_P2      = 0x3c,
            p_PD_TRANS_TIME_TO_P2        = 0x64,

            # RX OOB Signaling Attributes
            p_SAS_MAX_COM                = 64,
            p_SAS_MIN_COM                = 36,
            p_SATA_BURST_SEQ_LEN         = 0b0101,
            p_SATA_BURST_VAL             = 0b100,
            p_SATA_EIDLE_VAL             = 0b100,
            p_SATA_MAX_BURST             = 8,
            p_SATA_MAX_INIT              = 21,
            p_SATA_MAX_WAKE              = 7,
            p_SATA_MIN_BURST             = 4,
            p_SATA_MIN_INIT              = 12,
            p_SATA_MIN_WAKE              = 4,

            # RX Fabric Clock Output Control Attributes
            p_TRANS_TIME_RATE            = 0x0E,

            # TX Buffer Attributes
            p_TXBUF_EN                   = "TRUE" if tx_buffer_enable else "FALSE",
            p_TXBUF_RESET_ON_RATE_CHANGE = "TRUE",
            p_TXDLY_CFG                  = 0x001F,
            p_TXDLY_LCFG                 = 0x030,
            p_TXDLY_TAP_CFG              = 0x0000,
            p_TXPH_CFG                   = 0x0780,
            p_TXPHDLY_CFG                = 0x084020,
            p_TXPH_MONITOR_SEL           = 0b00000,
            p_TX_XCLK_SEL                = "TXOUT" if tx_buffer_enable else "TXUSR",

            # FPGA TX Interface Attributes
            p_TX_DATA_WIDTH              = data_width,

            # TX Configurable Driver Attributes
            p_TX_DEEMPH0                 = 0b000000,
            p_TX_DEEMPH1                 = 0b000000,
            p_TX_EIDLE_ASSERT_DELAY      = 0b110,
            p_TX_EIDLE_DEASSERT_DELAY    = 0b100,
            p_TX_LOOPBACK_DRIVE_HIZ      = "FALSE",
            p_TX_MAINCURSOR_SEL          = 0b0,
            p_TX_DRIVE_MODE              = "DIRECT",
            p_TX_MARGIN_FULL_0           = 0b1001110,
            p_TX_MARGIN_FULL_1           = 0b1001001,
            p_TX_MARGIN_FULL_2           = 0b1000101,
            p_TX_MARGIN_FULL_3           = 0b1000010,
            p_TX_MARGIN_FULL_4           = 0b1000000,
            p_TX_MARGIN_LOW_0            = 0b1000110,
            p_TX_MARGIN_LOW_1            = 0b1000100,
            p_TX_MARGIN_LOW_2            = 0b1000010,
            p_TX_MARGIN_LOW_3            = 0b1000000,
            p_TX_MARGIN_LOW_4            = 0b1000000,

            # TX Gearbox Attributes
            p_TXGEARBOX_EN               = "FALSE",

            # TX Initialization and Reset Attributes
            p_TXPCSRESET_TIME            = 0b00001,
            p_TXPMARESET_TIME            = 0b00001,

            # TX Receiver Detection Attributes
            p_TX_RXDETECT_CFG            = 0x1832,
            p_TX_RXDETECT_REF            = 0b100,

            # JTAG Attributes
            p_ACJTAG_DEBUG_MODE          = 0b0,
            p_ACJTAG_MODE                = 0b0,
            p_ACJTAG_RESET               = 0b0,

            # CDR Attributes
            p_CFOK_CFG                   = 0x49000040E80,
            p_CFOK_CFG2                  = 0b0100000,
            p_CFOK_CFG3                  = 0b0100000,
            p_CFOK_CFG4                  = 0b0,
            p_CFOK_CFG5                  = 0x0,
            p_CFOK_CFG6                  = 0b0000,
            p_RXOSCALRESET_TIME          = 0b00011,
            p_RXOSCALRESET_TIMEOUT       = 0b00000,

            # PMA Attributes
            p_CLK_COMMON_SWING           = 0b0,
            p_RX_CLKMUX_EN               = 0b1,
            p_TX_CLKMUX_EN               = 0b1,
            p_ES_CLK_PHASE_SEL           = 0b0,
            p_USE_PCS_CLK_PHASE_SEL      = 0b0,
            p_PMA_RSV6                   = 0b0,
            p_PMA_RSV7                   = 0b0,

            # TX Configuration Driver Attributes
            p_TX_PREDRIVER_MODE          = 0b0,
            p_PMA_RSV5                   = 0b0,
            p_SATA_PLL_CFG               = "VCO_3000MHZ",

            # RX Fabric Clock Output Control Attributes
            p_RXOUT_DIV                  = qpll.config["d"],

            # TX Fabric Clock Output Control Attributes
            p_TXOUT_DIV                  = qpll.config["d"],

            # RX Phase Interpolator Attributes
            p_RXPI_CFG0                  = 0b000,
            p_RXPI_CFG1                  = 0b1,
            p_RXPI_CFG2                  = 0b1,

            # RX Equalizer Attributes
            p_ADAPT_CFG0                 = 0x00000,
            p_RXLPMRESET_TIME            = 0b0001111,
            p_RXLPM_BIAS_STARTUP_DISABLE = 0b0,
            p_RXLPM_CFG                  = 0b0110,
            p_RXLPM_CFG1                 = 0b0,
            p_RXLPM_CM_CFG               = 0b0,
            p_RXLPM_GC_CFG               = 0b111100010,
            p_RXLPM_GC_CFG2              = 0b001,
            p_RXLPM_HF_CFG               = 0b00001111110000,
            p_RXLPM_HF_CFG2              = 0b01010,
            p_RXLPM_HF_CFG3              = 0b0000,
            p_RXLPM_HOLD_DURING_EIDLE    = 0b0,
            p_RXLPM_INCM_CFG             = 0b0,
            p_RXLPM_IPCM_CFG             = 0b1,
            p_RXLPM_LF_CFG               = 0b000000001111110000,
            p_RXLPM_LF_CFG2              = 0b01010,
            p_RXLPM_OSINT_CFG            = 0b100,

            # TX Phase Interpolator PPM Controller Attributes
            p_TXPI_CFG0                  = 0b00,
            p_TXPI_CFG1                  = 0b00,
            p_TXPI_CFG2                  = 0b00,
            p_TXPI_CFG3                  = 0b0,
            p_TXPI_CFG4                  = 0b0,
            p_TXPI_CFG5                  = 0b000,
            p_TXPI_GREY_SEL              = 0b0,
            p_TXPI_INVSTROBE_SEL         = 0b0,
            p_TXPI_PPMCLK_SEL            = "TXUSRCLK2",
            p_TXPI_PPM_CFG               = 0x00,
            p_TXPI_SYNFREQ_PPM           = 0b001,

            # LOOPBACK Attributes
            p_LOOPBACK_CFG               = 0b0,
            p_PMA_LOOPBACK_CFG           = 0b0,

            # RX OOB Signalling Attributes
            p_RXOOB_CLK_CFG              = "PMA",

            # TX OOB Signalling Attributes
            p_TXOOB_CFG                  = 0b0,

            # RX Buffer Attributes
            p_RXSYNC_MULTILANE           = 0b0,
            p_RXSYNC_OVRD                = 0b0,
            p_RXSYNC_SKIP_DA             = 0b0,

            # TX Buffer Attributes
            p_TXSYNC_MULTILANE           = 0b0,
            p_TXSYNC_OVRD                = 0b1 if tx_buffer_enable else 0b0,
            p_TXSYNC_SKIP_DA             = 0b0
        )
        self.gtp_params.update(
            # CPLL Ports
            i_GTRSVD                = 0b0000000000000000,
            i_PCSRSVDIN             = 0b0000000000000000,
            i_TSTIN                 = 0b11111111111111111111,

            # Channel - DRP Ports
            i_DRPADDR               = drp_mux.addr,
            i_DRPCLK                = drp_mux.clk,
            i_DRPDI                 = drp_mux.di,
            o_DRPDO                 = drp_mux.do,
            i_DRPEN                 = drp_mux.en,
            o_DRPRDY                = drp_mux.rdy,
            i_DRPWE                 = drp_mux.we,

            # Clocking Ports
            i_RXSYSCLKSEL           = 0b00 if qpll.channel == 0 else 0b11,
            i_TXSYSCLKSEL           = 0b00 if qpll.channel == 0 else 0b11,

            # FPGA TX Interface Datapath Configuration
            i_TX8B10BEN             = 0,

            # GTPE2_CHANNEL Clocking Ports
            i_PLL0CLK               = qpll.clk if qpll.channel == 0 else 0,
            i_PLL0REFCLK            = qpll.refclk if qpll.channel == 0 else 0,
            i_PLL1CLK               = qpll.clk if qpll.channel == 1 else 0,
            i_PLL1REFCLK            = qpll.refclk if qpll.channel == 1 else 0,

            # Loopback Ports
            i_LOOPBACK              = self.loopback,

            # PCI Express Ports
            #o_PHYSTATUS            =,
            i_RXRATE                = 0,
            #o_RXVALID              =,

            # PMA Reserved Ports
            i_PMARSVDIN3            = 0b0,
            i_PMARSVDIN4            = 0b0,

            # Power-Down Ports
            i_RXPD                  = Cat(rx_init.gtrxpd, rx_init.gtrxpd),
            i_TXPD                  = 0b00,

            # RX 8B/10B Decoder Ports
            i_SETERRSTATUS          = 0,

            # RX Initialization and Reset Ports
            i_EYESCANRESET          = 0,
            i_RXUSERRDY             = rx_init.rxuserrdy,

            # RX Margin Analysis Ports
            #o_EYESCANDATAERROR     =,
            i_EYESCANMODE           = 0,
            i_EYESCANTRIGGER        = 0,

            # Receive Ports
            i_CLKRSVD0              = 0,
            i_CLKRSVD1              = 0,
            i_DMONFIFORESET         = 0,
            i_DMONITORCLK           = 0,
            o_RXPMARESETDONE        = rx_init.rxpmaresetdone,
            i_SIGVALIDCLK           = 0,

            # Receive Ports - CDR Ports
            i_RXCDRFREQRESET        = 0,
            i_RXCDRHOLD             = 0,
            #o_RXCDRLOCK            =,
            i_RXCDROVRDEN           = 0,
            i_RXCDRRESET            = 0,
            i_RXCDRRESETRSV         = 0,
            i_RXOSCALRESET          = 0,
            i_RXOSINTCFG            = 0b0010,
            #o_RXOSINTDONE          =,
            i_RXOSINTHOLD           = 0,
            i_RXOSINTOVRDEN         = 0,
            i_RXOSINTPD             = 0,
            #o_RXOSINTSTARTED       =,
            i_RXOSINTSTROBE         = 0,
            #o_RXOSINTSTROBESTARTED =,
            i_RXOSINTTESTOVRDEN     = 0,

            # Receive Ports - Clock Correction Ports
            #o_RXCLKCORCNT          =,

            # Receive Ports - FPGA RX Interface Datapath Configuration
            i_RX8B10BEN             = 0,

            # Receive Ports - FPGA RX Interface Ports
            o_RXDATA                = Cat(*[rxdata[10*i:10*i+8] for i in range(nwords)]),
            i_RXUSRCLK              = ClockSignal("rx"),
            i_RXUSRCLK2             = ClockSignal("rx"),

            # Receive Ports - Pattern Checker Ports
            #o_RXPRBSERR            =,
            i_RXPRBSSEL             = 0,

            # Receive Ports - Pattern Checker ports
            i_RXPRBSCNTRESET        = 0,

            # Receive Ports - RX 8B/10B Decoder Ports
            #o_RXCHARISCOMMA        =,
            o_RXCHARISK             = Cat(*[rxdata[10*i+8] for i in range(nwords)]),
            o_RXDISPERR             = Cat(*[rxdata[10*i+9] for i in range(nwords)]),
            #o_RXNOTINTABLE         =,

            # Receive Ports - RX AFE Ports
            i_GTPRXN                = rx_pads.n,
            i_GTPRXP                = rx_pads.p,
            i_PMARSVDIN2            = 0b0,
            #o_PMARSVDOUT0          =,
            #o_PMARSVDOUT1          =,

            # Receive Ports - RX Buffer Bypass Ports
            i_RXBUFRESET            = 0,
            #o_RXBUFSTATUS          =,
            i_RXDDIEN               = 0 if rx_buffer_enable else 1,
            i_RXDLYBYPASS           = 1 if rx_buffer_enable else 0,
            i_RXDLYEN               = 0,
            i_RXDLYOVRDEN           = 0,
            i_RXDLYSRESET           = rx_init.rxdlysreset,
            o_RXDLYSRESETDONE       = rx_init.rxdlysresetdone,
            i_RXPHALIGN             = 0,
            o_RXPHALIGNDONE         = rxphaligndone,
            i_RXPHALIGNEN           = 0,
            i_RXPHDLYPD             = 0,
            i_RXPHDLYRESET          = 0,
            #o_RXPHMONITOR          =,
            i_RXPHOVRDEN            = 0,
            #o_RXPHSLIPMONITOR      =,
            #o_RXSTATUS             =,
            i_RXSYNCALLIN           = rxphaligndone,
            o_RXSYNCDONE            = rx_init.rxsyncdone,
            i_RXSYNCIN              = 0,
            i_RXSYNCMODE            = 0 if rx_buffer_enable else 1,
            #o_RXSYNCOUT            =,

            # Receive Ports - RX Byte and Word Alignment Ports
            #o_RXBYTEISALIGNED      =,
            #o_RXBYTEREALIGN        =,
            #o_RXCOMMADET           =,
            i_RXCOMMADETEN          = 1,
            i_RXMCOMMAALIGNEN       = (~clock_aligner & self.rx_align & (rx_prbs_config == 0b00)) if rx_buffer_enable else 0,
            i_RXPCOMMAALIGNEN       = (~clock_aligner & self.rx_align & (rx_prbs_config == 0b00)) if rx_buffer_enable else 0,
            i_RXSLIDE               = 0,

            # Receive Ports - RX Channel Bonding Ports
            #o_RXCHANBONDSEQ        =,
            i_RXCHBONDEN            = 0,
            i_RXCHBONDI             = 0b0000,
            i_RXCHBONDLEVEL         = 0,
            i_RXCHBONDMASTER        = 0,
            #o_RXCHBONDO            =,
            i_RXCHBONDSLAVE         = 0,

            # Receive Ports - RX Channel Bonding Ports
            #o_RXCHANISALIGNED      =,
            #o_RXCHANREALIGN        =,

            # Receive Ports - RX Decision Feedback Equalizer
            #o_DMONITOROUT          =,
            i_RXADAPTSELTEST        = 0,
            i_RXDFEXYDEN            = 0,
            i_RXOSINTEN             = 0b1,
            i_RXOSINTID0            = 0,
            i_RXOSINTNTRLEN         = 0,
            #o_RXOSINTSTROBEDONE    =,

            # Receive Ports - RX Driver,OOB signalling,Coupling and Eq.,CDR
            i_RXLPMLFOVRDEN         = 0,
            i_RXLPMOSINTNTRLEN      = 0,

            # Receive Ports - RX Equalizer Ports
            i_RXLPMHFHOLD           = 0,
            i_RXLPMHFOVRDEN         = 0,
            i_RXLPMLFHOLD           = 0,
            i_RXOSHOLD              = 0,
            i_RXOSOVRDEN            = 0,

            # Receive Ports - RX Fabric ClocK Output Control Ports
            #o_RXRATEDONE           =,

            # Receive Ports - RX Fabric Clock Output Control Ports
            i_RXRATEMODE            = 0b0,

            # Receive Ports - RX Fabric Output Control Ports
            o_RXOUTCLK              = self.rxoutclk,
            #o_RXOUTCLKFABRIC       =,
            #o_RXOUTCLKPCS          =,
            i_RXOUTCLKSEL           = 0b010,

            # Receive Ports - RX Gearbox Ports
            #o_RXDATAVALID          =,
            #o_RXHEADER             =,
            #o_RXHEADERVALID        =,
            #o_RXSTARTOFSEQ         =,
            i_RXGEARBOXSLIP         = 0,

            # Receive Ports - RX Initialization and Reset Ports
            i_GTRXRESET             = rx_init.gtrxreset,
            i_RXLPMRESET            = 0,
            i_RXOOBRESET            = 0,
            i_RXPCSRESET            = 0,
            i_RXPMARESET            = 0,

            # Receive Ports - RX OOB Signaling ports
            #o_RXCOMSASDET          =,
            #o_RXCOMWAKEDET         =,
            #o_RXCOMINITDET         =,
            #o_RXELECIDLE           =,
            i_RXELECIDLEMODE        = 0b11,

            # Receive Ports - RX Polarity Control Ports
            i_RXPOLARITY            = rx_polarity,

            # Receive Ports -RX Initialization and Reset Ports
            o_RXRESETDONE           = rx_init.rxresetdone,

            # TX Buffer Bypass Ports
            i_TXPHDLYTSTCLK         = 0,

            # TX Configurable Driver Ports
            i_TXPOSTCURSOR          = 0b00000,
            i_TXPOSTCURSORINV       = 0,
            i_TXPRECURSOR           = 0b00000,
            i_TXPRECURSORINV        = 0,

            # TX Fabric Clock Output Control Ports
            i_TXRATEMODE            = 0,

            # TX Initialization and Reset Ports
            i_CFGRESET              = 0,
            i_GTTXRESET             = tx_init.gttxreset,
            #o_PCSRSVDOUT           =,
            i_TXUSERRDY             = tx_init.txuserrdy,

            # TX Phase Interpolator PPM Controller Ports
            i_TXPIPPMEN             = 0,
            i_TXPIPPMOVRDEN         = 0,
            i_TXPIPPMPD             = 0,
            i_TXPIPPMSEL            = 1,
            i_TXPIPPMSTEPSIZE       = 0,

            # Transceiver Reset Mode Operation
            i_GTRESETSEL            = 0,
            i_RESETOVRD             = 0,

            # Transmit Ports
            #o_TXPMARESETDONE       =,

            # Transmit Ports - Configurable Driver Ports
            i_PMARSVDIN0            = 0b0,
            i_PMARSVDIN1            = 0b0,

            # Transmit Ports - FPGA TX Interface Ports
            i_TXDATA                = Cat(*[txdata[10*i:10*i+8] for i in range(nwords)]),
            i_TXUSRCLK              = ClockSignal("tx"),
            i_TXUSRCLK2             = ClockSignal("tx"),

            # Transmit Ports - PCI Express Ports
            i_TXELECIDLE            = 0,
            i_TXMARGIN              = 0,
            i_TXRATE                = 0,
            i_TXSWING               = 0,

            # Transmit Ports - Pattern Generator Ports
            i_TXPRBSFORCEERR        = 0,

            # Transmit Ports - TX 8B/10B Encoder Ports
            i_TX8B10BBYPASS         = 0,
            i_TXCHARDISPMODE        = Cat(*[txdata[10*i+9] for i in range(nwords)]),
            i_TXCHARDISPVAL         = Cat(*[txdata[10*i+8] for i in range(nwords)]),
            i_TXCHARISK             = 0,

            # Transmit Ports - TX Buffer Bypass Ports
            i_TXDLYBYPASS           = 1 if tx_buffer_enable else 0,
            i_TXDLYEN               = tx_init.txdlyen,
            i_TXDLYHOLD             = 0,
            i_TXDLYOVRDEN           = 0,
            i_TXDLYSRESET           = tx_init.txdlysreset,
            o_TXDLYSRESETDONE       = tx_init.txdlysresetdone,
            i_TXDLYUPDOWN           = 0,
            i_TXPHALIGN             = tx_init.txphalign,
            o_TXPHALIGNDONE         = tx_init.txphaligndone,
            i_TXPHALIGNEN           = 0 if tx_buffer_enable else 1,
            i_TXPHDLYPD             = 0,
            i_TXPHDLYRESET          = 0,
            i_TXPHINIT              = tx_init.txphinit,
            o_TXPHINITDONE          = tx_init.txphinitdone,
            i_TXPHOVRDEN            = 0,

            # Transmit Ports - TX Buffer Ports
            #o_TXBUFSTATUS          =,

            # Transmit Ports - TX Buffer and Phase Alignment Ports
            i_TXSYNCALLIN           = 0,
            #o_TXSYNCDONE           =,
            #i_TXSYNCIN             = 0,
            #i_TXSYNCMODE           = 0,
            #o_TXSYNCOUT            =,

            # Transmit Ports - TX Configurable Driver Ports
            o_GTPTXN                = tx_pads.n,
            o_GTPTXP                = tx_pads.p,
            i_TXBUFDIFFCTRL         = 0b100,
            i_TXDEEMPH              = 0,
            i_TXDIFFCTRL            = 0b1000,
            i_TXDIFFPD              = 0,
            i_TXINHIBIT             = self.tx_inhibit,
            i_TXMAINCURSOR          = 0b0000000,
            i_TXPISOPD              = 0,

            # Transmit Ports - TX Fabric Clock Output Control Ports
            o_TXOUTCLK              = self.txoutclk,
            #o_TXOUTCLKFABRIC       =,
            #o_TXOUTCLKPCS          =,
            i_TXOUTCLKSEL           = 0b010 if tx_buffer_enable else 0b011,
            #o_TXRATEDONE           =,

            # Transmit Ports - TX Gearbox Ports
            #o_TXGEARBOXREADY       =,
            i_TXHEADER              = 0,
            i_TXSEQUENCE            = 0,
            i_TXSTARTSEQ            = 0,

            # Transmit Ports - TX Initialization and Reset Ports
            i_TXPCSRESET            = 0,
            i_TXPMARESET            = 0,
            o_TXRESETDONE           = tx_init.txresetdone,

            # Transmit Ports - TX OOB signalling Ports
            #o_TXCOMFINISH          =,
            i_TXCOMINIT             = 0,
            i_TXCOMSAS              = 0,
            i_TXCOMWAKE             = 0,
            i_TXPDELECIDLEMODE      = 0,

            # Transmit Ports - TX Polarity Control Ports
            i_TXPOLARITY            = tx_polarity,

            # Transmit Ports - TX Receiver Detection Ports
            i_TXDETECTRX            = 0,

            # Transmit Ports - pattern Generator Ports
            i_TXPRBSSEL             = 0,
        )

        # TX clocking ------------------------------------------------------------------------------
        tx_reset_deglitched = Signal()
        tx_reset_deglitched.attr.add("no_retiming")
        self.sync += tx_reset_deglitched.eq(~tx_init.done)
        self.clock_domains.cd_tx = ClockDomain()

        txoutclk_bufg = Signal()
        self.specials += Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_bufg)

        if not tx_buffer_enable:
            txoutclk_div = qpll.config["clkin"]/self.tx_clk_freq
        else:
            txoutclk_div = 1
        # Use txoutclk_bufg when divider is 1
        if txoutclk_div == 1:
            self.comb += self.cd_tx.clk.eq(txoutclk_bufg)
            self.specials += AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
        # Use a BUFR when integer divider (with BUFR_DIVIDE)
        elif txoutclk_div == int(txoutclk_div):
            txoutclk_bufr = Signal()
            self.specials += [
                Instance("BUFR", i_I=txoutclk_bufg, o_O=txoutclk_bufr,
                    i_CE=1, p_BUFR_DIVIDE=str(int(txoutclk_div))),
                Instance("BUFG", i_I=txoutclk_bufr, o_O=self.cd_tx.clk),
                AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
            ]
        # Use a PLL when non-integer divider
        else:
            txoutclk_pll = S7PLL()
            self.comb += txoutclk_pll.reset.eq(tx_reset_deglitched)
            self.submodules += txoutclk_pll
            txoutclk_pll.register_clkin(txoutclk_bufg, qpll.config["clkin"])
            txoutclk_pll.create_clkout(self.cd_tx, self.tx_clk_freq)

        # RX clocking ------------------------------------------------------------------------------
        rx_reset_deglitched = Signal()
        rx_reset_deglitched.attr.add("no_retiming")
        self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
        self.clock_domains.cd_rx = ClockDomain()
        self.specials += [
            Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
            AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
        ]

        # TX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(data_width, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.comb += [
            self.tx_prbs.i.eq(Cat(*[self.encoder.output[i] for i in range(nwords)])),
            If(tx_produce_square_wave,
                # square wave @ linerate/data_width for scope observation
                txdata.eq(Signal(data_width, reset=(1<<(data_width//2))-1))
            ).Elif(tx_produce_pattern,
                txdata.eq(self.tx_pattern)
            ).Else(
                txdata.eq(self.tx_prbs.o)
            )
        ]

        # RX Datapath and PRBS ---------------------------------------------------------------------
        self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(data_width, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        for i in range(nwords):
            self.comb += self.decoders[i].input.eq(rxdata[10*i:10*(i+1)])
        self.comb += self.rx_prbs.i.eq(rxdata)

        # Clock Aligner ----------------------------------------------------------------------------
        if clock_aligner:
            clock_aligner = BruteforceClockAligner(clock_aligner_comma, self.tx_clk_freq, check_period=10e-3)
            self.submodules.clock_aligner = clock_aligner
            ps_restart = PulseSynchronizer("tx", "sys")
            self.submodules += ps_restart
            self.comb += [
                clock_aligner.rxdata.eq(rxdata),
                ps_restart.i.eq(clock_aligner.restart),
                rx_init.restart.eq((ps_restart.o & self.rx_align) | ~self.rx_enable),
                self.rx_ready.eq(clock_aligner.ready)
            ]
Example #7
0
    def __init__(self, pll, pads, mode="master"):
        self.tx_data = Signal(32)
        self.rx_data = Signal(32)

        self.tx_idle = Signal()
        self.tx_comma = Signal()
        self.rx_idle = Signal()
        self.rx_comma = Signal()

        self.rx_bitslip_value = Signal(6)
        self.rx_delay_rst = Signal()
        self.rx_delay_inc = Signal()
        self.rx_delay_ce = Signal()

        # # #

        self.submodules.encoder = ClockDomainsRenamer("serwb_serdes")(Encoder(
            4, True))
        self.decoders = [
            ClockDomainsRenamer("serwb_serdes")(Decoder(True))
            for _ in range(4)
        ]
        self.submodules += self.decoders

        # clocking:

        # In master mode:
        # - linerate/10 pll refclk provided by user
        # - linerate/10 slave refclk generated on clk_pads
        # In Slave mode:
        # - linerate/10 pll refclk provided by clk_pads
        self.clock_domains.cd_serwb_serdes = ClockDomain()
        self.clock_domains.cd_serwb_serdes_5x = ClockDomain()
        self.clock_domains.cd_serwb_serdes_20x = ClockDomain(reset_less=True)
        self.comb += [
            self.cd_serwb_serdes.clk.eq(pll.serwb_serdes_clk),
            self.cd_serwb_serdes_5x.clk.eq(pll.serwb_serdes_5x_clk),
            self.cd_serwb_serdes_20x.clk.eq(pll.serwb_serdes_20x_clk)
        ]
        self.specials += AsyncResetSynchronizer(self.cd_serwb_serdes,
                                                ~pll.lock)
        self.comb += self.cd_serwb_serdes_5x.rst.eq(self.cd_serwb_serdes.rst)

        # control/status cdc
        tx_idle = Signal()
        tx_comma = Signal()
        rx_idle = Signal()
        rx_comma = Signal()
        rx_bitslip_value = Signal(6)
        self.specials += [
            MultiReg(self.tx_idle, tx_idle, "serwb_serdes"),
            MultiReg(self.tx_comma, tx_comma, "serwb_serdes"),
            MultiReg(rx_idle, self.rx_idle, "sys"),
            MultiReg(rx_comma, self.rx_comma, "sys")
        ]
        self.specials += MultiReg(self.rx_bitslip_value, rx_bitslip_value,
                                  "serwb_serdes"),

        # tx clock (linerate/10)
        if mode == "master":
            self.submodules.tx_clk_gearbox = Gearbox(40, "serwb_serdes", 8,
                                                     "serwb_serdes_5x")
            self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30)
                                                  | (0b1111100000 << 20)
                                                  | (0b1111100000 << 10)
                                                  | (0b1111100000 << 0))
            clk_o = Signal()
            self.specials += [
                Instance("OSERDESE2",
                         p_DATA_WIDTH=8,
                         p_TRISTATE_WIDTH=1,
                         p_DATA_RATE_OQ="DDR",
                         p_DATA_RATE_TQ="BUF",
                         p_SERDES_MODE="MASTER",
                         o_OQ=clk_o,
                         i_OCE=1,
                         i_RST=ResetSignal("serwb_serdes"),
                         i_CLK=ClockSignal("serwb_serdes_20x"),
                         i_CLKDIV=ClockSignal("serwb_serdes_5x"),
                         i_D1=self.tx_clk_gearbox.o[0],
                         i_D2=self.tx_clk_gearbox.o[1],
                         i_D3=self.tx_clk_gearbox.o[2],
                         i_D4=self.tx_clk_gearbox.o[3],
                         i_D5=self.tx_clk_gearbox.o[4],
                         i_D6=self.tx_clk_gearbox.o[5],
                         i_D7=self.tx_clk_gearbox.o[6],
                         i_D8=self.tx_clk_gearbox.o[7]),
                Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n)
            ]

        # tx datapath
        # tx_data -> encoders -> gearbox -> serdes
        self.submodules.tx_gearbox = Gearbox(40, "serwb_serdes", 8,
                                             "serwb_serdes_5x")
        self.comb += [
            If(tx_comma, self.encoder.k[0].eq(1),
               self.encoder.d[0].eq(0xbc)).Else(
                   self.encoder.d[0].eq(self.tx_data[0:8]),
                   self.encoder.d[1].eq(self.tx_data[8:16]),
                   self.encoder.d[2].eq(self.tx_data[16:24]),
                   self.encoder.d[3].eq(self.tx_data[24:32]))
        ]
        self.sync.serwb_serdes += \
            If(tx_idle,
                self.tx_gearbox.i.eq(0)
            ).Else(
                self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)]))
            )

        serdes_o = Signal()
        self.specials += [
            Instance("OSERDESE2",
                     p_DATA_WIDTH=8,
                     p_TRISTATE_WIDTH=1,
                     p_DATA_RATE_OQ="DDR",
                     p_DATA_RATE_TQ="BUF",
                     p_SERDES_MODE="MASTER",
                     o_OQ=serdes_o,
                     i_OCE=1,
                     i_RST=ResetSignal("serwb_serdes"),
                     i_CLK=ClockSignal("serwb_serdes_20x"),
                     i_CLKDIV=ClockSignal("serwb_serdes_5x"),
                     i_D1=self.tx_gearbox.o[0],
                     i_D2=self.tx_gearbox.o[1],
                     i_D3=self.tx_gearbox.o[2],
                     i_D4=self.tx_gearbox.o[3],
                     i_D5=self.tx_gearbox.o[4],
                     i_D6=self.tx_gearbox.o[5],
                     i_D7=self.tx_gearbox.o[6],
                     i_D8=self.tx_gearbox.o[7]),
            Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n)
        ]

        # rx clock
        use_bufr = True
        if mode == "slave":
            clk_i = Signal()
            clk_i_bufg = Signal()
            self.specials += [
                Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i)
            ]
            if use_bufr:
                clk_i_bufr = Signal()
                self.specials += [
                    Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
                    Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
                ]
            else:
                self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
            self.comb += pll.refclk.eq(clk_i_bufg)

        # rx datapath
        # serdes -> gearbox -> bitslip -> decoders -> rx_data
        self.submodules.rx_gearbox = Gearbox(8, "serwb_serdes_5x", 40,
                                             "serwb_serdes")
        self.submodules.rx_bitslip = ClockDomainsRenamer("serwb_serdes")(
            BitSlip(40))

        serdes_i_nodelay = Signal()
        self.specials += [
            Instance("IBUFDS_DIFF_OUT",
                     i_I=pads.rx_p,
                     i_IB=pads.rx_n,
                     o_O=serdes_i_nodelay)
        ]

        serdes_i_delayed = Signal()
        serdes_q = Signal(8)
        self.specials += [
            Instance("IDELAYE2",
                     p_DELAY_SRC="IDATAIN",
                     p_SIGNAL_PATTERN="DATA",
                     p_CINVCTRL_SEL="FALSE",
                     p_HIGH_PERFORMANCE_MODE="TRUE",
                     p_REFCLK_FREQUENCY=200.0,
                     p_PIPE_SEL="FALSE",
                     p_IDELAY_TYPE="VARIABLE",
                     p_IDELAY_VALUE=0,
                     i_C=ClockSignal(),
                     i_LD=self.rx_delay_rst,
                     i_CE=self.rx_delay_ce,
                     i_LDPIPEEN=0,
                     i_INC=self.rx_delay_inc,
                     i_IDATAIN=serdes_i_nodelay,
                     o_DATAOUT=serdes_i_delayed),
            Instance("ISERDESE2",
                     p_DATA_WIDTH=8,
                     p_DATA_RATE="DDR",
                     p_SERDES_MODE="MASTER",
                     p_INTERFACE_TYPE="NETWORKING",
                     p_NUM_CE=1,
                     p_IOBDELAY="IFD",
                     i_DDLY=serdes_i_delayed,
                     i_CE1=1,
                     i_RST=ResetSignal("serwb_serdes"),
                     i_CLK=ClockSignal("serwb_serdes_20x"),
                     i_CLKB=~ClockSignal("serwb_serdes_20x"),
                     i_CLKDIV=ClockSignal("serwb_serdes_5x"),
                     i_BITSLIP=0,
                     o_Q8=serdes_q[0],
                     o_Q7=serdes_q[1],
                     o_Q6=serdes_q[2],
                     o_Q5=serdes_q[3],
                     o_Q4=serdes_q[4],
                     o_Q3=serdes_q[5],
                     o_Q2=serdes_q[6],
                     o_Q1=serdes_q[7])
        ]

        self.comb += [
            self.rx_gearbox.i.eq(serdes_q),
            self.rx_bitslip.value.eq(rx_bitslip_value),
            self.rx_bitslip.i.eq(self.rx_gearbox.o),
            self.decoders[0].input.eq(self.rx_bitslip.o[0:10]),
            self.decoders[1].input.eq(self.rx_bitslip.o[10:20]),
            self.decoders[2].input.eq(self.rx_bitslip.o[20:30]),
            self.decoders[3].input.eq(self.rx_bitslip.o[30:40]),
            self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])),
            rx_idle.eq(self.rx_bitslip.o == 0),
            rx_comma.eq(
                ((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1))
                & ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0))
                & ((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0))
                & ((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0)))
        ]
    def __init__(self, pll, pads, mode="master"):
        self.tx_pattern = CSRStorage(20)
        self.tx_produce_square_wave = CSRStorage()
        self.tx_prbs_config = CSRStorage(2)

        self.rx_pattern = CSRStatus(20)
        self.rx_prbs_config = CSRStorage(2)
        self.rx_prbs_errors = CSRStatus(32)

        self.rx_bitslip_value = CSRStorage(5)
        self.rx_delay_rst = CSR()
        self.rx_delay_en_vtc = CSRStorage(reset=1)
        self.rx_delay_inc = CSRStorage()
        self.rx_delay_ce = CSR()
        self.rx_delay_m_cntvalueout = CSRStatus(9)
        self.rx_delay_s_cntvalueout = CSRStatus(9)

        # # #

        self.submodules.encoder = ClockDomainsRenamer("serdes")(Encoder(
            2, True))
        self.decoders = [
            ClockDomainsRenamer("serdes")(Decoder(True)) for _ in range(2)
        ]
        self.submodules += self.decoders

        # clocking
        # master mode:
        # - linerate/10 pll refclk provided externally
        # - linerate/10 clock generated on clk_pads
        # slave mode:
        # - linerate/10 pll refclk provided by clk_pads
        self.clock_domains.cd_serdes = ClockDomain()
        self.clock_domains.cd_serdes_10x = ClockDomain()
        self.clock_domains.cd_serdes_10x_90 = ClockDomain()
        self.clock_domains.cd_serdes_2p5x = ClockDomain()
        self.comb += [
            self.cd_serdes.clk.eq(pll.serdes_clk),
            self.cd_serdes_10x.clk.eq(pll.serdes_10x_clk),
            self.cd_serdes_10x_90.clk.eq(pll.serdes_10x_90_clk),
            self.cd_serdes_2p5x.clk.eq(pll.serdes_2p5x_clk)
        ]
        self.specials += [
            AsyncResetSynchronizer(self.cd_serdes, ~pll.lock),
            AsyncResetSynchronizer(self.cd_serdes_10x, ~pll.lock),
            AsyncResetSynchronizer(self.cd_serdes_10x_90, ~pll.lock),
            AsyncResetSynchronizer(self.cd_serdes_2p5x, ~pll.lock)
        ]

        # control/status cdc
        tx_pattern = Signal(20)
        tx_produce_square_wave = Signal()
        tx_prbs_config = Signal(2)

        rx_pattern = Signal(20)
        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        rx_bitslip_value = Signal(5)
        rx_delay_rst = Signal()
        rx_delay_inc = Signal()
        rx_delay_en_vtc = Signal()
        rx_delay_ce = Signal()
        rx_delay_m_cntvalueout = Signal(9)
        rx_delay_s_cntvalueout = Signal(9)

        self.specials += [
            MultiReg(self.tx_pattern.storage, tx_pattern, "serdes"),
            MultiReg(self.tx_produce_square_wave.storage,
                     tx_produce_square_wave, "serdes"),
            MultiReg(self.tx_prbs_config.storage, tx_prbs_config, "serdes")
        ]

        self.specials += [
            MultiReg(rx_pattern, self.rx_pattern.status, "sys"),
            MultiReg(self.rx_prbs_config.storage, rx_prbs_config, "serdes"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors.status,
                     "sys")  # FIXME
        ]

        self.specials += [
            MultiReg(self.rx_bitslip_value.storage, rx_bitslip_value,
                     "serdes"),
            MultiReg(self.rx_delay_inc.storage, rx_delay_inc, "serdes_2p5x"),
            MultiReg(self.rx_delay_en_vtc.storage, rx_delay_en_vtc,
                     "serdes_2p5x")
        ]
        self.submodules.do_rx_delay_rst = PulseSynchronizer(
            "sys", "serdes_2p5x")
        self.comb += [
            rx_delay_rst.eq(self.do_rx_delay_rst.o),
            self.do_rx_delay_rst.i.eq(self.rx_delay_rst.re)
        ]
        self.submodules.do_rx_delay_ce = PulseSynchronizer(
            "sys", "serdes_2p5x")
        self.comb += [
            rx_delay_ce.eq(self.do_rx_delay_ce.o),
            self.do_rx_delay_ce.i.eq(self.rx_delay_ce.re)
        ]
        self.specials += [
            MultiReg(rx_delay_m_cntvalueout,
                     self.rx_delay_m_cntvalueout.status, "sys"),
            MultiReg(rx_delay_s_cntvalueout,
                     self.rx_delay_s_cntvalueout.status, "sys"),
        ]

        # tx clock (linerate/10)
        if mode == "master":
            self.submodules.tx_clk_gearbox = Gearbox(20, "serdes", 8,
                                                     "serdes_2p5x")
            self.comb += self.tx_clk_gearbox.i.eq(0b11111000001111100000)

            clk_o = Signal()
            self.specials += [
                Instance("OSERDESE3",
                         p_DATA_WIDTH=8,
                         p_INIT=0,
                         p_IS_CLK_INVERTED=0,
                         p_IS_CLKDIV_INVERTED=0,
                         p_IS_RST_INVERTED=0,
                         o_OQ=clk_o,
                         i_RST=ResetSignal("serdes_2p5x"),
                         i_CLK=ClockSignal("serdes_10x"),
                         i_CLKDIV=ClockSignal("serdes_2p5x"),
                         i_D=self.tx_clk_gearbox.o),
                Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n)
            ]

        # tx data and prbs
        self.submodules.tx_prbs = ClockDomainsRenamer("serdes")(PRBSTX(
            20, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.submodules.tx_gearbox = Gearbox(20, "serdes", 8, "serdes_2p5x")
        self.sync.serdes += [
            self.tx_prbs.i.eq(Cat(*[self.encoder.output[i]
                                    for i in range(2)])),
            If(tx_pattern != 0, self.tx_gearbox.i.eq(tx_pattern)).Elif(
                tx_produce_square_wave,
                # square wave @ linerate/20 for scope observation
                self.tx_gearbox.i.eq(0b11111111110000000000)).Else(
                    self.tx_gearbox.i.eq(self.tx_prbs.o))
        ]

        serdes_o = Signal()
        self.specials += [
            Instance("OSERDESE3",
                     p_DATA_WIDTH=8,
                     p_INIT=0,
                     p_IS_CLK_INVERTED=0,
                     p_IS_CLKDIV_INVERTED=0,
                     p_IS_RST_INVERTED=0,
                     o_OQ=serdes_o,
                     i_RST=ResetSignal("serdes_2p5x"),
                     i_CLK=ClockSignal("serdes_10x"),
                     i_CLKDIV=ClockSignal("serdes_2p5x"),
                     i_D=self.tx_gearbox.o),
            Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n)
        ]

        # rx clock
        use_bufr = True
        if mode == "slave":
            clk_i = Signal()
            clk_i_bufg = Signal()
            self.specials += [
                Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i)
            ]
            if use_bufr:
                clk_i_bufr = Signal()
                self.specials += [
                    Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
                    Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg),
                ]
            else:
                self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg),
            self.comb += pll.refclk.eq(clk_i_bufg)

        # rx
        self.submodules.rx_gearbox = Gearbox(8, "serdes_2p5x", 20, "serdes")
        self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(20))

        self.submodules.phase_detector = ClockDomainsRenamer("serdes_2p5x")(
            PhaseDetector())

        # use 2 serdes for phase detection: 1 master/ 1 slave
        serdes_m_i_nodelay = Signal()
        serdes_s_i_nodelay = Signal()
        self.specials += [
            Instance(
                "IBUFDS_DIFF_OUT",
                i_I=pads.rx_p,
                i_IB=pads.rx_n,
                o_O=serdes_m_i_nodelay,
                o_OB=serdes_s_i_nodelay,
            )
        ]

        serdes_m_i_delayed = Signal()
        serdes_m_q = Signal(8)
        self.specials += [
            Instance(
                "IDELAYE3",
                p_CASCADE="NONE",
                p_UPDATE_MODE="ASYNC",
                p_REFCLK_FREQUENCY=200.0,
                p_IS_CLK_INVERTED=0,
                p_IS_RST_INVERTED=0,
                # Note: can't use TIME mode since not reloading DELAY_VALUE on rst...
                p_DELAY_FORMAT="COUNT",
                p_DELAY_SRC="IDATAIN",
                p_DELAY_TYPE="VARIABLE",
                p_DELAY_VALUE=50,  # 1/4 bit period (ambient temp)
                i_CLK=ClockSignal("serdes_2p5x"),
                i_RST=rx_delay_rst,
                i_LOAD=0,
                i_INC=rx_delay_inc,
                i_EN_VTC=rx_delay_en_vtc,
                i_CE=rx_delay_ce,
                i_IDATAIN=serdes_m_i_nodelay,
                o_DATAOUT=serdes_m_i_delayed,
                o_CNTVALUEOUT=rx_delay_m_cntvalueout),
            Instance("ISERDESE3",
                     p_DATA_WIDTH=8,
                     i_D=serdes_m_i_delayed,
                     i_RST=ResetSignal("serdes_2p5x"),
                     i_FIFO_RD_CLK=0,
                     i_FIFO_RD_EN=0,
                     i_CLK=ClockSignal("serdes_10x"),
                     i_CLK_B=~ClockSignal("serdes_10x"),
                     i_CLKDIV=ClockSignal("serdes_2p5x"),
                     o_Q=serdes_m_q),
        ]
        self.comb += self.phase_detector.mdata.eq(serdes_m_q)

        serdes_s_i_delayed = Signal()
        serdes_s_q = Signal(8)
        self.specials += [
            Instance(
                "IDELAYE3",
                p_CASCADE="NONE",
                p_UPDATE_MODE="ASYNC",
                p_REFCLK_FREQUENCY=200.0,
                p_IS_CLK_INVERTED=0,
                p_IS_RST_INVERTED=0,
                # Note: can't use TIME mode since not reloading DELAY_VALUE on rst...
                p_DELAY_FORMAT="COUNT",
                p_DELAY_SRC="IDATAIN",
                p_DELAY_TYPE="VARIABLE",
                p_DELAY_VALUE=100,  # 1/2 bit period (ambient temp)
                i_CLK=ClockSignal("serdes_2p5x"),
                i_RST=rx_delay_rst,
                i_LOAD=0,
                i_INC=rx_delay_inc,
                i_EN_VTC=rx_delay_en_vtc,
                i_CE=rx_delay_ce,
                i_IDATAIN=serdes_s_i_nodelay,
                o_DATAOUT=serdes_s_i_delayed,
                o_CNTVALUEOUT=rx_delay_s_cntvalueout),
            Instance("ISERDESE3",
                     p_DATA_WIDTH=8,
                     i_D=serdes_s_i_delayed,
                     i_RST=ResetSignal("serdes_2p5x"),
                     i_FIFO_RD_CLK=0,
                     i_FIFO_RD_EN=0,
                     i_CLK=ClockSignal("serdes_10x"),
                     i_CLK_B=~ClockSignal("serdes_10x"),
                     i_CLKDIV=ClockSignal("serdes_2p5x"),
                     o_Q=serdes_s_q),
        ]
        self.comb += self.phase_detector.sdata.eq(~serdes_s_q)

        # rx data and prbs
        self.submodules.rx_prbs = ClockDomainsRenamer("serdes")(PRBSRX(
            20, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        self.comb += [
            self.rx_gearbox.i.eq(serdes_m_q),
            self.rx_bitslip.value.eq(rx_bitslip_value),
            self.rx_bitslip.i.eq(self.rx_gearbox.o),
            self.decoders[0].input.eq(self.rx_bitslip.o[:10]),
            self.decoders[1].input.eq(self.rx_bitslip.o[10:]),
            rx_pattern.eq(self.rx_bitslip.o),
            self.rx_prbs.i.eq(self.rx_bitslip.o)
        ]
Example #9
0
    def __init__(self, pll, pads, mode="master"):
        self.tx_data = Signal(32)
        self.rx_data = Signal(32)

        self.tx_idle = Signal()
        self.tx_comma = Signal()
        self.rx_idle = Signal()
        self.rx_comma = Signal()

        self.rx_bitslip_value = Signal(6)
        self.rx_delay_rst = Signal()
        self.rx_delay_inc = Signal()
        self.rx_delay_ce = Signal()
        self.rx_delay_en_vtc = Signal()

        # # #

        self.submodules.encoder = ClockDomainsRenamer("serwb_serdes")(Encoder(
            4, True))
        self.decoders = [
            ClockDomainsRenamer("serwb_serdes")(Decoder(True))
            for _ in range(4)
        ]
        self.submodules += self.decoders

        # clocking:

        # In master mode:
        # - linerate/10 pll refclk provided by user
        # - linerate/10 slave refclk generated on clk_pads
        # In Slave mode:
        # - linerate/10 pll refclk provided by clk_pads
        self.clock_domains.cd_serwb_serdes = ClockDomain()
        self.clock_domains.cd_serwb_serdes_5x = ClockDomain()
        self.clock_domains.cd_serwb_serdes_20x = ClockDomain(reset_less=True)
        self.comb += [
            self.cd_serwb_serdes.clk.eq(pll.serwb_serdes_clk),
            self.cd_serwb_serdes_5x.clk.eq(pll.serwb_serdes_5x_clk),
            self.cd_serwb_serdes_20x.clk.eq(pll.serwb_serdes_20x_clk)
        ]
        self.specials += AsyncResetSynchronizer(self.cd_serwb_serdes,
                                                ~pll.lock)
        self.comb += self.cd_serwb_serdes_5x.rst.eq(self.cd_serwb_serdes.rst)

        # control/status cdc
        tx_idle = Signal()
        tx_comma = Signal()
        rx_idle = Signal()
        rx_comma = Signal()
        rx_bitslip_value = Signal(6)
        rx_delay_rst = Signal()
        rx_delay_inc = Signal()
        rx_delay_en_vtc = Signal()
        rx_delay_ce = Signal()
        self.specials += [
            MultiReg(self.tx_idle, tx_idle, "serwb_serdes"),
            MultiReg(self.tx_comma, tx_comma, "serwb_serdes"),
            MultiReg(rx_idle, self.rx_idle, "sys"),
            MultiReg(rx_comma, self.rx_comma, "sys"),
            MultiReg(self.rx_bitslip_value, rx_bitslip_value, "serwb_serdes"),
            MultiReg(self.rx_delay_inc, rx_delay_inc, "serwb_serdes_5x"),
            MultiReg(self.rx_delay_en_vtc, rx_delay_en_vtc, "serwb_serdes_5x")
        ]
        self.submodules.do_rx_delay_rst = PulseSynchronizer(
            "sys", "serwb_serdes_5x")
        self.comb += [
            rx_delay_rst.eq(self.do_rx_delay_rst.o),
            self.do_rx_delay_rst.i.eq(self.rx_delay_rst)
        ]
        self.submodules.do_rx_delay_ce = PulseSynchronizer(
            "sys", "serwb_serdes_5x")
        self.comb += [
            rx_delay_ce.eq(self.do_rx_delay_ce.o),
            self.do_rx_delay_ce.i.eq(self.rx_delay_ce)
        ]

        # tx clock (linerate/10)
        if mode == "master":
            self.submodules.tx_clk_gearbox = Gearbox(40, "serwb_serdes", 8,
                                                     "serwb_serdes_5x")
            self.comb += self.tx_clk_gearbox.i.eq((0b1111100000 << 30)
                                                  | (0b1111100000 << 20)
                                                  | (0b1111100000 << 10)
                                                  | (0b1111100000 << 0))
            clk_o = Signal()
            self.specials += [
                Instance("OSERDESE3",
                         p_DATA_WIDTH=8,
                         p_INIT=0,
                         p_IS_CLK_INVERTED=0,
                         p_IS_CLKDIV_INVERTED=0,
                         p_IS_RST_INVERTED=0,
                         o_OQ=clk_o,
                         i_RST=ResetSignal("serwb_serdes"),
                         i_CLK=ClockSignal("serwb_serdes_20x"),
                         i_CLKDIV=ClockSignal("serwb_serdes_5x"),
                         i_D=self.tx_clk_gearbox.o),
                Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n)
            ]

        # tx datapath
        # tx_data -> encoders -> gearbox -> serdes
        self.submodules.tx_gearbox = Gearbox(40, "serwb_serdes", 8,
                                             "serwb_serdes_5x")
        self.comb += [
            If(tx_comma, self.encoder.k[0].eq(1),
               self.encoder.d[0].eq(0xbc)).Else(
                   self.encoder.d[0].eq(self.tx_data[0:8]),
                   self.encoder.d[1].eq(self.tx_data[8:16]),
                   self.encoder.d[2].eq(self.tx_data[16:24]),
                   self.encoder.d[3].eq(self.tx_data[24:32]))
        ]
        self.sync.serwb_serdes += \
            If(tx_idle,
                self.tx_gearbox.i.eq(0)
            ).Else(
                self.tx_gearbox.i.eq(Cat(*[self.encoder.output[i] for i in range(4)]))
            )

        serdes_o = Signal()
        self.specials += [
            Instance("OSERDESE3",
                     p_DATA_WIDTH=8,
                     p_INIT=0,
                     p_IS_CLK_INVERTED=0,
                     p_IS_CLKDIV_INVERTED=0,
                     p_IS_RST_INVERTED=0,
                     o_OQ=serdes_o,
                     i_RST=ResetSignal("serwb_serdes"),
                     i_CLK=ClockSignal("serwb_serdes_20x"),
                     i_CLKDIV=ClockSignal("serwb_serdes_5x"),
                     i_D=self.tx_gearbox.o),
            Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n)
        ]

        # rx clock
        use_bufr = True
        if mode == "slave":
            clk_i = Signal()
            clk_i_bufg = Signal()
            self.specials += [
                Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i)
            ]
            if use_bufr:
                clk_i_bufr = Signal()
                self.specials += [
                    Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
                    Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg)
                ]
            else:
                self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg)
            self.comb += pll.refclk.eq(clk_i_bufg)

        # rx datapath
        # serdes -> gearbox -> bitslip -> decoders -> rx_data
        self.submodules.rx_gearbox = Gearbox(8, "serwb_serdes_5x", 40,
                                             "serwb_serdes")
        self.submodules.rx_bitslip = ClockDomainsRenamer("serwb_serdes")(
            BitSlip(40))

        serdes_i_nodelay = Signal()
        self.specials += [
            Instance("IBUFDS_DIFF_OUT",
                     i_I=pads.rx_p,
                     i_IB=pads.rx_n,
                     o_O=serdes_i_nodelay)
        ]

        serdes_i_delayed = Signal()
        serdes_q = Signal(8)
        self.specials += [
            Instance("IDELAYE3",
                     p_CASCADE="NONE",
                     p_UPDATE_MODE="ASYNC",
                     p_REFCLK_FREQUENCY=200.0,
                     p_IS_CLK_INVERTED=0,
                     p_IS_RST_INVERTED=0,
                     p_DELAY_FORMAT="COUNT",
                     p_DELAY_SRC="IDATAIN",
                     p_DELAY_TYPE="VARIABLE",
                     p_DELAY_VALUE=0,
                     i_CLK=ClockSignal("serwb_serdes_5x"),
                     i_RST=rx_delay_rst,
                     i_LOAD=0,
                     i_INC=rx_delay_inc,
                     i_EN_VTC=rx_delay_en_vtc,
                     i_CE=rx_delay_ce,
                     i_IDATAIN=serdes_i_nodelay,
                     o_DATAOUT=serdes_i_delayed),
            Instance("ISERDESE3",
                     p_DATA_WIDTH=8,
                     i_D=serdes_i_delayed,
                     i_RST=ResetSignal("serwb_serdes"),
                     i_FIFO_RD_CLK=0,
                     i_FIFO_RD_EN=0,
                     i_CLK=ClockSignal("serwb_serdes_20x"),
                     i_CLK_B=~ClockSignal("serwb_serdes_20x"),
                     i_CLKDIV=ClockSignal("serwb_serdes_5x"),
                     o_Q=serdes_q)
        ]

        self.comb += [
            self.rx_gearbox.i.eq(serdes_q),
            self.rx_bitslip.value.eq(rx_bitslip_value),
            self.rx_bitslip.i.eq(self.rx_gearbox.o),
            self.decoders[0].input.eq(self.rx_bitslip.o[0:10]),
            self.decoders[1].input.eq(self.rx_bitslip.o[10:20]),
            self.decoders[2].input.eq(self.rx_bitslip.o[20:30]),
            self.decoders[3].input.eq(self.rx_bitslip.o[30:40]),
            self.rx_data.eq(Cat(*[self.decoders[i].d for i in range(4)])),
            rx_idle.eq(self.rx_bitslip.o == 0),
            rx_comma.eq(
                ((self.decoders[0].d == 0xbc) & (self.decoders[0].k == 1))
                & ((self.decoders[1].d == 0x00) & (self.decoders[1].k == 0))
                & ((self.decoders[2].d == 0x00) & (self.decoders[2].k == 0))
                & ((self.decoders[3].d == 0x00) & (self.decoders[3].k == 0)))
        ]
Example #10
0
    def __init__(self,
                 pll,
                 tx_pads,
                 rx_pads,
                 sys_clk_freq,
                 clock_aligner=True,
                 internal_loopback=False,
                 tx_polarity=0,
                 rx_polarity=0):
        self.tx_produce_square_wave = CSRStorage()
        self.tx_prbs_config = CSRStorage(2)

        self.rx_prbs_config = CSRStorage(2)
        self.rx_prbs_errors = CSRStatus(32)

        self.restart = CSR()
        self.ready = CSRStatus(2)

        # # #

        use_cpll = isinstance(pll, GTHChannelPLL)
        use_qpll0 = isinstance(pll,
                               GTHQuadPLL) and pll.config["qpll"] == "qpll0"
        use_qpll1 = isinstance(pll,
                               GTHQuadPLL) and pll.config["qpll"] == "qpll1"

        self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(2, True))
        self.decoders = [
            ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(2)
        ]
        self.submodules += self.decoders

        self.tx_ready = Signal()
        self.rx_ready = Signal()

        # transceiver direct clock outputs
        # useful to specify clock constraints in a way palatable to Vivado
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        self.tx_clk_freq = pll.config["linerate"] / 20
        self.rx_clk_freq = pll.config["linerate"] / 20

        # control/status cdc
        tx_produce_square_wave = Signal()
        tx_prbs_config = Signal(2)

        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        self.specials += [
            MultiReg(self.tx_produce_square_wave.storage,
                     tx_produce_square_wave, "tx"),
            MultiReg(self.tx_prbs_config.storage, tx_prbs_config, "tx"),
        ]

        self.specials += [
            MultiReg(self.rx_prbs_config.storage, rx_prbs_config, "rx"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors.status,
                     "sys"),  # FIXME
        ]

        # # #

        # TX generates RTIO clock, init must be in system domain
        tx_init = GTHInit(sys_clk_freq, False)
        self.comb += [
            tx_init.restart.eq(self.restart.re),
            self.tx_ready.eq(tx_init.done)
        ]
        # RX receives restart commands from RTIO domain
        rx_init = ClockDomainsRenamer("tx")(GTHInit(self.tx_clk_freq, True))
        self.submodules += tx_init, rx_init
        self.comb += [
            tx_init.plllock.eq(pll.lock),
            rx_init.plllock.eq(pll.lock),
            pll.reset.eq(tx_init.pllreset),
            self.ready.status.eq(Cat(self.tx_ready, self.rx_ready))
        ]

        txdata = Signal(20)
        rxdata = Signal(20)
        rxphaligndone = Signal()
        self.specials += \
            Instance("GTHE3_CHANNEL",
                # Reset modes
                i_GTRESETSEL=0,
                i_RESETOVRD=0,

                # PMA Attributes
                p_PMA_RSV1=0xf800,
                p_RX_BIAS_CFG0=0x0AB4,
                p_RX_CM_TRIM=0b1010,
                p_RX_CLK25_DIV=5,
                p_TX_CLK25_DIV=5,

                # Power-Down Attributes
                p_PD_TRANS_TIME_FROM_P2=0x3c,
                p_PD_TRANS_TIME_NONE_P2=0x19,
                p_PD_TRANS_TIME_TO_P2=0x64,

                # CPLL
                p_CPLL_CFG0=0x67f8,
                p_CPLL_CFG1=0xa4ac,
                p_CPLL_CFG2=0xf007,
                p_CPLL_CFG3=0x0000,
                p_CPLL_FBDIV=1 if (use_qpll0 | use_qpll1) else pll.config["n2"],
                p_CPLL_FBDIV_45=4 if (use_qpll0 | use_qpll1) else pll.config["n1"],
                p_CPLL_REFCLK_DIV=1 if (use_qpll0 | use_qpll1) else pll.config["m"],
                p_RXOUT_DIV=pll.config["d"],
                p_TXOUT_DIV=pll.config["d"],
                i_CPLLRESET=0,
                i_CPLLPD=0 if (use_qpll0 | use_qpll1) else pll.reset,
                o_CPLLLOCK=Signal() if (use_qpll0 | use_qpll1) else pll.lock,
                i_CPLLLOCKEN=1,
                i_CPLLREFCLKSEL=0b001,
                i_TSTIN=2**20-1,
                i_GTREFCLK0=0 if (use_qpll0 | use_qpll1) else pll.refclk,

                # QPLL
                i_QPLL0CLK=0 if (use_cpll | use_qpll1) else pll.clk,
                i_QPLL0REFCLK=0 if (use_cpll | use_qpll1) else pll.refclk,
                i_QPLL1CLK=0 if (use_cpll | use_qpll0) else pll.clk,
                i_QPLL1REFCLK=0 if (use_cpll | use_qpll0) else pll.refclk,

                # TX clock
                p_TXBUF_EN="FALSE",
                p_TX_XCLK_SEL="TXUSR",
                o_TXOUTCLK=self.txoutclk,
                i_TXSYSCLKSEL=0b00 if use_cpll else 0b10 if use_qpll0 else 0b11,
                i_TXPLLCLKSEL=0b00 if use_cpll else 0b11 if use_qpll0 else 0b10,
                i_TXOUTCLKSEL=0b11,

                # TX Startup/Reset
                i_GTTXRESET=tx_init.gtXxreset,
                o_TXRESETDONE=tx_init.Xxresetdone,
                i_TXDLYSRESET=tx_init.Xxdlysreset,
                o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
                o_TXPHALIGNDONE=tx_init.Xxphaligndone,
                i_TXUSERRDY=tx_init.Xxuserrdy,
                i_TXSYNCMODE=1,

                # TX data
                p_TX_DATA_WIDTH=20,
                p_TX_INT_DATAWIDTH=0,
                i_TXCTRL0=Cat(txdata[8], txdata[18]),
                i_TXCTRL1=Cat(txdata[9], txdata[19]),
                i_TXDATA=Cat(txdata[:8], txdata[10:18]),
                i_TXUSRCLK=ClockSignal("tx"),
                i_TXUSRCLK2=ClockSignal("tx"),

                # TX electrical
                i_TXPD=0b00,
                p_TX_CLKMUX_EN=1,
                i_TXBUFDIFFCTRL=0b000,
                i_TXDIFFCTRL=0b1100,

                # Internal Loopback
                i_LOOPBACK=0b010 if internal_loopback else 0b000,

                # RX Startup/Reset
                i_GTRXRESET=rx_init.gtXxreset,
                o_RXRESETDONE=rx_init.Xxresetdone,
                i_RXDLYSRESET=rx_init.Xxdlysreset,
                o_RXPHALIGNDONE=rxphaligndone,
                i_RXSYNCALLIN=rxphaligndone,
                i_RXUSERRDY=rx_init.Xxuserrdy,
                i_RXSYNCIN=0,
                i_RXSYNCMODE=1,
                o_RXSYNCDONE=rx_init.Xxsyncdone,

                # RX AFE
                i_RXDFEAGCCTRL=1,
                i_RXDFEXYDEN=1,
                i_RXLPMEN=1,
                i_RXOSINTCFG=0xd,
                i_RXOSINTEN=1,

                # RX clock
                i_RXRATE=0,
                i_RXDLYBYPASS=0,
                p_RXBUF_EN="FALSE",
                p_RX_XCLK_SEL="RXUSR",
                i_RXSYSCLKSEL=0b00,
                i_RXOUTCLKSEL=0b010,
                i_RXPLLCLKSEL=0b00,
                o_RXOUTCLK=self.rxoutclk,
                i_RXUSRCLK=ClockSignal("rx"),
                i_RXUSRCLK2=ClockSignal("rx"),

                # RX Clock Correction Attributes
                p_CLK_CORRECT_USE="FALSE",
                p_CLK_COR_SEQ_1_1=0b0100000000,
                p_CLK_COR_SEQ_2_1=0b0100000000,
                p_CLK_COR_SEQ_1_ENABLE=0b1111,
                p_CLK_COR_SEQ_2_ENABLE=0b1111,

                # RX data
                p_RX_DATA_WIDTH=20,
                p_RX_INT_DATAWIDTH=0,
                o_RXCTRL0=Cat(rxdata[8], rxdata[18]),
                o_RXCTRL1=Cat(rxdata[9], rxdata[19]),
                o_RXDATA=Cat(rxdata[:8], rxdata[10:18]),

                # RX electrical
                i_RXPD=0b00,
                p_RX_CLKMUX_EN=1,
                i_RXELECIDLEMODE=0b11,

                # Polarity
                i_TXPOLARITY=tx_polarity,
                i_RXPOLARITY=rx_polarity,

                # Pads
                i_GTHRXP=rx_pads.p,
                i_GTHRXN=rx_pads.n,
                o_GTHTXP=tx_pads.p,
                o_GTHTXN=tx_pads.n
            )

        # tx clocking
        tx_reset_deglitched = Signal()
        tx_reset_deglitched.attr.add("no_retiming")
        self.sync += tx_reset_deglitched.eq(~tx_init.done)
        self.clock_domains.cd_tx = ClockDomain()
        tx_bufg_div = pll.config["clkin"] / self.tx_clk_freq
        assert tx_bufg_div == int(tx_bufg_div)
        self.specials += [
            Instance("BUFG_GT",
                     i_I=self.txoutclk,
                     o_O=self.cd_tx.clk,
                     i_DIV=int(tx_bufg_div) - 1),
            AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
        ]

        # rx clocking
        rx_reset_deglitched = Signal()
        rx_reset_deglitched.attr.add("no_retiming")
        self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
        self.clock_domains.cd_rx = ClockDomain()
        self.specials += [
            Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
            AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
        ]

        # tx data and prbs
        self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(20, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.comb += [
            self.tx_prbs.i.eq(Cat(*[self.encoder.output[i]
                                    for i in range(2)])),
            If(
                tx_produce_square_wave,
                # square wave @ linerate/20 for scope observation
                txdata.eq(0b11111111110000000000)).Else(
                    txdata.eq(self.tx_prbs.o))
        ]

        # rx data and prbs
        self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(20, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        self.comb += [
            self.decoders[0].input.eq(rxdata[:10]),
            self.decoders[1].input.eq(rxdata[10:]),
            self.rx_prbs.i.eq(rxdata)
        ]

        # clock alignment
        if clock_aligner:
            clock_aligner = BruteforceClockAligner(0b0101111100,
                                                   self.tx_clk_freq)
            self.submodules += clock_aligner
            self.comb += [
                clock_aligner.rxdata.eq(rxdata),
                rx_init.restart.eq(clock_aligner.restart),
                self.rx_ready.eq(clock_aligner.ready)
            ]
        else:
            self.comb += self.rx_ready.eq(rx_init.done)
Example #11
0
    def __init__(self, pll, tx_pads, sys_clk_freq, polarity=0):
        self.prbs_config = Signal(2)

        self.produce_square_wave = CSRStorage()

        self.txdiffcttrl = CSRStorage(4, reset=0b1000)
        self.txmaincursor = CSRStorage(7, reset=80)
        self.txprecursor = CSRStorage(5)
        self.txpostcursor = CSRStorage(5)

        # # #

        use_cpll = isinstance(pll, GTXChannelPLL)
        use_qpll = isinstance(pll, GTXQuadPLL)

        self.submodules.init = GTXInit(sys_clk_freq, False)
        self.comb += [
            self.init.plllock.eq(pll.lock),
            pll.reset.eq(self.init.pllreset)
        ]

        nwords = 40 // 10

        txoutclk = Signal()
        txdata = Signal(40)
        self.specials += \
            Instance("GTXE2_CHANNEL",
                # PMA Attributes
                p_PMA_RSV=0x00018480,
                p_PMA_RSV2=0x2050,
                p_PMA_RSV3=0,
                p_PMA_RSV4=0,
                p_RX_BIAS_CFG=0b100,
                p_RX_CM_TRIM=0b010,
                p_RX_OS_CFG=0b10000000,
                p_RX_CLK25_DIV=5,
                p_TX_CLK25_DIV=5,

                # Power-Down Attributes
                p_PD_TRANS_TIME_FROM_P2=0x3c,
                p_PD_TRANS_TIME_NONE_P2=0x3c,
                p_PD_TRANS_TIME_TO_P2=0x64,

                # CPLL
                p_CPLL_CFG=0xBC07DC,
                p_CPLL_FBDIV=1 if use_qpll else pll.config["n2"],
                p_CPLL_FBDIV_45=4 if use_qpll else pll.config["n1"],
                p_CPLL_REFCLK_DIV=1 if use_qpll else pll.config["m"],
                p_RXOUT_DIV=pll.config["d"],
                p_TXOUT_DIV=pll.config["d"],
                i_CPLLRESET=0 if use_qpll else pll.reset,
                o_CPLLLOCK=Signal() if use_qpll else pll.lock,
                i_CPLLLOCKEN=1,
                i_CPLLREFCLKSEL=0b001,
                i_TSTIN=2**20-1,
                i_GTREFCLK0=0 if use_qpll else pll.refclk,

                # QPLL
                i_QPLLCLK=0 if use_cpll else pll.clk,
                i_QPLLREFCLK=0 if use_cpll else pll.refclk,

                # TX clock
                p_TXBUF_EN="FALSE",
                p_TX_XCLK_SEL="TXUSR",
                o_TXOUTCLK=txoutclk,
                i_TXSYSCLKSEL=0b11 if use_qpll else 0b00,
                i_TXOUTCLKSEL=0b11,

                # disable RX
                i_RXPD=0b11,

                # Startup/Reset
                i_GTTXRESET=self.init.gtXxreset,
                o_TXRESETDONE=self.init.Xxresetdone,
                i_TXDLYSRESET=self.init.Xxdlysreset,
                o_TXDLYSRESETDONE=self.init.Xxdlysresetdone,
                o_TXPHALIGNDONE=self.init.Xxphaligndone,
                i_TXUSERRDY=self.init.Xxuserrdy,

                # TX data
                p_TX_DATA_WIDTH=40,
                p_TX_INT_DATAWIDTH=1,
                i_TXCHARDISPMODE=Cat(*[txdata[10*i+9] for i in range(nwords)]),
                i_TXCHARDISPVAL=Cat(*[txdata[10*i+8] for i in range(nwords)]),
                i_TXDATA=Cat(*[txdata[10*i:10*i+8] for i in range(nwords)]),
                i_TXUSRCLK=ClockSignal("tx"),
                i_TXUSRCLK2=ClockSignal("tx"),

                # TX electrical
                i_TXBUFDIFFCTRL=0b100,
                i_TXDIFFCTRL=self.txdiffcttrl.storage,
                p_TX_MAINCURSOR_SEL=1,
                i_TXMAINCURSOR=self.txmaincursor.storage,
                i_TXPRECURSOR=self.txprecursor.storage,
                i_TXPOSTCURSOR=self.txpostcursor.storage,

                # Polarity
                i_TXPOLARITY=polarity,

                # Pads
                o_GTXTXP=tx_pads.txp,
                o_GTXTXN=tx_pads.txn
            )

        self.clock_domains.cd_tx = ClockDomain()
        self.specials += Instance("BUFH", i_I=txoutclk, o_O=self.cd_tx.clk)
        self.specials += AsyncResetSynchronizer(self.cd_tx, ~self.init.done)

        self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(
            nwords, True))
        self.submodules.prbs = ClockDomainsRenamer("tx")(PRBSTX(40, True))
        self.comb += [
            self.prbs.config.eq(self.prbs_config),
            self.prbs.i.eq(
                Cat(*[self.encoder.output[i] for i in range(nwords)])),
            If(
                self.produce_square_wave.storage,
                # square wave @ linerate/40 for scope observation
                txdata.eq(0b1111111111111111111100000000000000000000)).Else(
                    txdata.eq(self.prbs.o))
        ]
Example #12
0
    def __init__(self,
                 pll,
                 tx_pads,
                 rx_pads,
                 sys_clk_freq,
                 dw=20,
                 mode="master"):
        assert (dw == 20) or (dw == 40)
        assert mode in ["master", "slave"]

        # # #

        nwords = dw // 10

        use_cpll = isinstance(pll, GTHChannelPLL)
        use_qpll0 = isinstance(pll,
                               GTHQuadPLL) and pll.config["qpll"] == "qpll0"
        use_qpll1 = isinstance(pll,
                               GTHQuadPLL) and pll.config["qpll"] == "qpll1"

        self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")(
            Encoder(nwords, True))
        self.submodules.decoders = decoders = [
            ClockDomainsRenamer("rtio_rx")((Decoder(True)))
            for _ in range(nwords)
        ]
        self.rx_ready = Signal()

        self.rtio_clk_freq = pll.config["linerate"] / dw

        # transceiver direct clock outputs
        # useful to specify clock constraints in a way palatable to Vivado
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        # # #

        # TX generates RTIO clock, init must be in system domain
        tx_init = GTHInit(sys_clk_freq, False)
        # RX receives restart commands from RTIO domain
        rx_init = ClockDomainsRenamer("rtio_tx")(GTHInit(
            self.rtio_clk_freq, True))
        self.submodules += tx_init, rx_init
        self.comb += [
            tx_init.plllock.eq(pll.lock),
            rx_init.plllock.eq(pll.lock)
        ]

        txdata = Signal(dw)
        rxdata = Signal(dw)
        rxphaligndone = Signal()
        self.specials += \
            Instance("GTHE3_CHANNEL",
                # Reset modes
                i_GTRESETSEL=0,
                i_RESETOVRD=0,

                # PMA Attributes
                p_PMA_RSV1=0xf800,
                p_RX_BIAS_CFG0=0x0AB4,
                p_RX_CM_TRIM=0b1010,
                p_RX_CLK25_DIV=5,
                p_TX_CLK25_DIV=5,

                # Power-Down Attributes
                p_PD_TRANS_TIME_FROM_P2=0x3c,
                p_PD_TRANS_TIME_NONE_P2=0x19,
                p_PD_TRANS_TIME_TO_P2=0x64,

                # CPLL
                p_CPLL_CFG0=0x67f8,
                p_CPLL_CFG1=0xa4ac,
                p_CPLL_CFG2=0xf007,
                p_CPLL_CFG3=0x0000,
                p_CPLL_FBDIV=1 if use_qpll0 or use_qpll1 else pll.config["n2"],
                p_CPLL_FBDIV_45=4 if use_qpll0 or use_qpll1 else pll.config["n1"],
                p_CPLL_REFCLK_DIV=1 if use_qpll0 or use_qpll1 else pll.config["m"],
                p_RXOUT_DIV=pll.config["d"],
                p_TXOUT_DIV=pll.config["d"],
                i_CPLLRESET=0,
                i_CPLLPD=0 if use_qpll0 or use_qpll1 else pll.reset,
                o_CPLLLOCK=Signal() if use_qpll0 or use_qpll1 else pll.lock,
                i_CPLLLOCKEN=1,
                i_CPLLREFCLKSEL=0b001,
                i_TSTIN=2**20-1,
                i_GTREFCLK0=0 if use_qpll0 or use_qpll1 else pll.refclk,

                # QPLL
                i_QPLL0CLK=0 if use_cpll or use_qpll1 else pll.clk,
                i_QPLL0REFCLK=0 if use_cpll or use_qpll1 else pll.refclk,
                i_QPLL1CLK=0 if use_cpll or use_qpll0 else pll.clk,
                i_QPLL1REFCLK=0 if use_cpll or use_qpll0 else pll.refclk,

                # TX clock
                p_TXBUF_EN="FALSE",
                p_TX_XCLK_SEL="TXUSR",
                o_TXOUTCLK=self.txoutclk,
                i_TXSYSCLKSEL=0b00 if use_cpll else 0b10 if use_qpll0 else 0b11,
                i_TXPLLCLKSEL=0b00 if use_cpll else 0b11 if use_qpll0 else 0b10,
                i_TXOUTCLKSEL=0b11,

                # TX Startup/Reset
                i_GTTXRESET=tx_init.gtXxreset,
                o_TXRESETDONE=tx_init.Xxresetdone,
                i_TXDLYSRESET=tx_init.Xxdlysreset,
                o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
                o_TXPHALIGNDONE=tx_init.Xxphaligndone,
                i_TXUSERRDY=tx_init.Xxuserrdy,
                i_TXSYNCMODE=1,

                # TX data
                p_TX_DATA_WIDTH=dw,
                p_TX_INT_DATAWIDTH=dw == 40,
                i_TXCTRL0=Cat(*[txdata[10*i+8] for i in range(nwords)]),
                i_TXCTRL1=Cat(*[txdata[10*i+9] for i in range(nwords)]),
                i_TXDATA=Cat(*[txdata[10*i:10*i+8] for i in range(nwords)]),
                i_TXUSRCLK=ClockSignal("rtio_tx"),
                i_TXUSRCLK2=ClockSignal("rtio_tx"),

                # TX electrical
                i_TXPD=0b00,
                p_TX_CLKMUX_EN=1,
                i_TXBUFDIFFCTRL=0b000,
                i_TXDIFFCTRL=0b1100,

                # RX Startup/Reset
                i_GTRXRESET=rx_init.gtXxreset,
                o_RXRESETDONE=rx_init.Xxresetdone,
                i_RXDLYSRESET=rx_init.Xxdlysreset,
                o_RXPHALIGNDONE=rxphaligndone,
                i_RXSYNCALLIN=rxphaligndone,
                i_RXUSERRDY=rx_init.Xxuserrdy,
                i_RXSYNCIN=0,
                i_RXSYNCMODE=1,
                o_RXSYNCDONE=rx_init.Xxsyncdone,

                # RX AFE
                i_RXDFEAGCCTRL=1,
                i_RXDFEXYDEN=1,
                i_RXLPMEN=1,
                i_RXOSINTCFG=0xd,
                i_RXOSINTEN=1,

                # RX clock
                i_RXRATE=0,
                i_RXDLYBYPASS=0,
                p_RXBUF_EN="FALSE",
                p_RX_XCLK_SEL="RXUSR",
                i_RXSYSCLKSEL=0b00,
                i_RXOUTCLKSEL=0b010,
                i_RXPLLCLKSEL=0b00,
                o_RXOUTCLK=self.rxoutclk,
                i_RXUSRCLK=ClockSignal("rtio_rx"),
                i_RXUSRCLK2=ClockSignal("rtio_rx"),

                # RX Clock Correction Attributes
                p_CLK_CORRECT_USE="FALSE",
                p_CLK_COR_SEQ_1_1=0b0100000000,
                p_CLK_COR_SEQ_2_1=0b0100000000,
                p_CLK_COR_SEQ_1_ENABLE=0b1111,
                p_CLK_COR_SEQ_2_ENABLE=0b1111,

                # RX data
                p_RX_DATA_WIDTH=dw,
                p_RX_INT_DATAWIDTH=dw == 40,
                o_RXCTRL0=Cat(*[rxdata[10*i+8] for i in range(nwords)]),
                o_RXCTRL1=Cat(*[rxdata[10*i+9] for i in range(nwords)]),
                o_RXDATA=Cat(*[rxdata[10*i:10*i+8] for i in range(nwords)]),

                # RX electrical
                i_RXPD=0b00,
                p_RX_CLKMUX_EN=1,
                i_RXELECIDLEMODE=0b11,

                # Pads
                i_GTHRXP=rx_pads.p,
                i_GTHRXN=rx_pads.n,
                o_GTHTXP=tx_pads.p,
                o_GTHTXN=tx_pads.n
            )

        # tx clocking
        tx_reset_deglitched = Signal()
        tx_reset_deglitched.attr.add("no_retiming")
        self.sync += tx_reset_deglitched.eq(~tx_init.done)
        self.clock_domains.cd_rtio_tx = ClockDomain()
        if mode is "master":
            tx_bufg_div = pll.config["clkin"] / self.rtio_clk_freq
            assert tx_bufg_div == int(tx_bufg_div)
            self.specials += \
                Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk,
                    i_DIV=int(tx_bufg_div)-1)
        self.specials += AsyncResetSynchronizer(self.cd_rtio_tx,
                                                tx_reset_deglitched)

        # rx clocking
        rx_reset_deglitched = Signal()
        rx_reset_deglitched.attr.add("no_retiming")
        self.sync.rtio_tx += rx_reset_deglitched.eq(~rx_init.done)
        self.clock_domains.cd_rtio_rx = ClockDomain()
        self.specials += [
            Instance("BUFG_GT", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk),
            AsyncResetSynchronizer(self.cd_rtio_rx, rx_reset_deglitched)
        ]

        # tx data
        self.comb += txdata.eq(Cat(*[encoder.output[i]
                                     for i in range(nwords)]))

        # rx data
        for i in range(nwords):
            self.comb += decoders[i].input.eq(rxdata[10 * i:10 * (i + 1)])

        # clock alignment
        clock_aligner = BruteforceClockAligner(0b0101111100,
                                               self.rtio_clk_freq)
        self.submodules += clock_aligner
        self.comb += [
            clock_aligner.rxdata.eq(rxdata),
            rx_init.restart.eq(clock_aligner.restart),
            self.rx_ready.eq(clock_aligner.ready)
        ]
Example #13
0
    def __init__(self,
                 qpll,
                 tx_pads,
                 rx_pads,
                 sys_clk_freq,
                 clock_aligner=True,
                 internal_loopback=False,
                 tx_polarity=0,
                 rx_polarity=0):
        self.tx_produce_square_wave = CSRStorage()
        self.tx_prbs_config = CSRStorage(2)

        self.rx_prbs_config = CSRStorage(2)
        self.rx_prbs_errors = CSRStatus(32)

        # # #

        self.submodules.encoder = ClockDomainsRenamer("tx")(Encoder(2, True))
        self.decoders = [
            ClockDomainsRenamer("rx")(Decoder(True)) for _ in range(2)
        ]
        self.submodules += self.decoders

        self.rx_ready = Signal()

        # transceiver direct clock outputs
        # useful to specify clock constraints in a way palatable to Vivado
        self.txoutclk = Signal()
        self.rxoutclk = Signal()

        self.tx_clk_freq = qpll.config["linerate"] / 20

        # control/status cdc
        tx_produce_square_wave = Signal()
        tx_prbs_config = Signal(2)

        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        self.specials += [
            MultiReg(self.tx_produce_square_wave.storage,
                     tx_produce_square_wave, "tx"),
            MultiReg(self.tx_prbs_config.storage, tx_prbs_config, "tx"),
        ]

        self.specials += [
            MultiReg(self.rx_prbs_config.storage, rx_prbs_config, "rx"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors.status,
                     "sys"),  # FIXME
        ]

        # # #

        # TX generates RTIO clock, init must be in system domain
        tx_init = GTPTXInit(sys_clk_freq)
        # RX receives restart commands from RTIO domain
        rx_init = ClockDomainsRenamer("tx")(GTPRXInit(self.tx_clk_freq))
        self.submodules += tx_init, rx_init
        # debug
        self.tx_init = tx_init
        self.rx_init = rx_init
        self.comb += [
            tx_init.plllock.eq(qpll.lock),
            rx_init.plllock.eq(qpll.lock),
            qpll.reset.eq(tx_init.pllreset)
        ]

        assert qpll.config["linerate"] < 6.6e9
        rxcdr_cfgs = {
            1: 0x0000107FE406001041010,
            2: 0x0000107FE206001041010,
            4: 0x0000107FE106001041010,
            8: 0x0000107FE086001041010
        }

        txdata = Signal(20)
        rxdata = Signal(20)
        rxphaligndone = Signal()
        self.specials += \
            Instance("GTPE2_CHANNEL",
                i_GTRESETSEL=0,
                i_RESETOVRD=0,
                p_SIM_RESET_SPEEDUP="FALSE",

                # DRP
                i_DRPADDR=rx_init.drpaddr,
                i_DRPCLK=ClockSignal("tx"),
                i_DRPDI=rx_init.drpdi,
                o_DRPDO=rx_init.drpdo,
                i_DRPEN=rx_init.drpen,
                o_DRPRDY=rx_init.drprdy,
                i_DRPWE=rx_init.drpwe,

                # PMA Attributes
                p_PMA_RSV=0x333,
                p_PMA_RSV2=0x2040,
                p_PMA_RSV3=0,
                p_PMA_RSV4=0,
                p_RX_BIAS_CFG=0b0000111100110011,
                p_RX_CM_SEL=0b01,
                p_RX_CM_TRIM=0b1010,
                p_RX_OS_CFG=0b10000000,
                p_RXLPM_IPCM_CFG=1,
                i_RXELECIDLEMODE=0b11,
                i_RXOSINTCFG=0b0010,
                i_RXOSINTEN=1,

                # Power-Down Attributes
                p_PD_TRANS_TIME_FROM_P2=0x3c,
                p_PD_TRANS_TIME_NONE_P2=0x3c,
                p_PD_TRANS_TIME_TO_P2=0x64,

                # QPLL
                i_PLL0CLK=qpll.clk,
                i_PLL0REFCLK=qpll.refclk,

                # TX clock
                p_TXBUF_EN="FALSE",
                p_TX_XCLK_SEL="TXUSR",
                o_TXOUTCLK=self.txoutclk,
                p_TXOUT_DIV=qpll.config["d"],
                i_TXSYSCLKSEL=0b00,
                i_TXOUTCLKSEL=0b11,

                # TX Startup/Reset
                i_GTTXRESET=tx_init.gttxreset,
                i_RXPD=Cat(rx_init.gtrxpd, rx_init.gtrxpd),
                o_TXRESETDONE=tx_init.txresetdone,
                p_TXSYNC_OVRD=1,
                i_TXDLYSRESET=tx_init.txdlysreset,
                o_TXDLYSRESETDONE=tx_init.txdlysresetdone,
                i_TXPHINIT=tx_init.txphinit,
                o_TXPHINITDONE=tx_init.txphinitdone,
                i_TXPHALIGNEN=1,
                i_TXPHALIGN=tx_init.txphalign,
                o_TXPHALIGNDONE=tx_init.txphaligndone,
                i_TXDLYEN=tx_init.txdlyen,
                i_TXUSERRDY=tx_init.txuserrdy,

                # TX data
                p_TX_DATA_WIDTH=20,
                i_TXCHARDISPMODE=Cat(txdata[9], txdata[19]),
                i_TXCHARDISPVAL=Cat(txdata[8], txdata[18]),
                i_TXDATA=Cat(txdata[:8], txdata[10:18]),
                i_TXUSRCLK=ClockSignal("tx"),
                i_TXUSRCLK2=ClockSignal("tx"),

                # TX electrical
                i_TXBUFDIFFCTRL=0b100,
                i_TXDIFFCTRL=0b1000,

                # Internal Loopback
                i_LOOPBACK=0b010 if internal_loopback else 0b000,

                # RX Startup/Reset
                i_GTRXRESET=rx_init.gtrxreset,
                o_RXRESETDONE=rx_init.rxresetdone,
                i_RXDLYSRESET=rx_init.rxdlysreset,
                o_RXDLYSRESETDONE=rx_init.rxdlysresetdone,
                o_RXPHALIGNDONE=rxphaligndone,
                i_RXSYNCALLIN=rxphaligndone,
                i_RXUSERRDY=rx_init.rxuserrdy,
                i_RXSYNCIN=0,
                i_RXSYNCMODE=1,
                p_RXSYNC_MULTILANE=0,
                p_RXSYNC_OVRD=0,
                o_RXSYNCDONE=rx_init.rxsyncdone,
                p_RXPMARESET_TIME=0b11,
                o_RXPMARESETDONE=rx_init.rxpmaresetdone,

                # RX clock
                p_RX_CLK25_DIV=5,
                p_TX_CLK25_DIV=5,
                p_RX_XCLK_SEL="RXUSR",
                p_RXOUT_DIV=qpll.config["d"],
                i_RXSYSCLKSEL=0b00,
                i_RXOUTCLKSEL=0b010,
                o_RXOUTCLK=self.rxoutclk,
                i_RXUSRCLK=ClockSignal("rx"),
                i_RXUSRCLK2=ClockSignal("rx"),
                p_RXCDR_CFG=rxcdr_cfgs[qpll.config["d"]],
                p_RXPI_CFG1=1,
                p_RXPI_CFG2=1,

                # RX Clock Correction Attributes
                p_CLK_CORRECT_USE="FALSE",

                # RX data
                p_RXBUF_EN="FALSE",
                p_RXDLY_CFG=0x001f,
                p_RXDLY_LCFG=0x030,
                p_RXPHDLY_CFG=0x084020,
                p_RXPH_CFG=0xc00002,
                p_RX_DATA_WIDTH=20,
                i_RXCOMMADETEN=1,
                i_RXDLYBYPASS=0,
                i_RXDDIEN=1,
                o_RXDISPERR=Cat(rxdata[9], rxdata[19]),
                o_RXCHARISK=Cat(rxdata[8], rxdata[18]),
                o_RXDATA=Cat(rxdata[:8], rxdata[10:18]),

                # Polarity
                i_TXPOLARITY=tx_polarity,
                i_RXPOLARITY=rx_polarity,

                # Pads
                i_GTPRXP=rx_pads.p,
                i_GTPRXN=rx_pads.n,
                o_GTPTXP=tx_pads.p,
                o_GTPTXN=tx_pads.n
            )

        # tx clocking
        tx_reset_deglitched = Signal()
        tx_reset_deglitched.attr.add("no_retiming")
        self.sync += tx_reset_deglitched.eq(~tx_init.done)
        self.clock_domains.cd_tx = ClockDomain()
        txoutclk_bufg = Signal()
        txoutclk_bufr = Signal()
        tx_bufr_div = qpll.config["clkin"] / self.tx_clk_freq
        assert tx_bufr_div == int(tx_bufr_div)
        self.specials += [
            Instance("BUFG", i_I=self.txoutclk, o_O=txoutclk_bufg),
            # TODO: use MMCM instead?
            Instance("BUFR",
                     i_I=txoutclk_bufg,
                     o_O=txoutclk_bufr,
                     i_CE=1,
                     p_BUFR_DIVIDE=str(int(tx_bufr_div))),
            Instance("BUFG", i_I=txoutclk_bufr, o_O=self.cd_tx.clk),
            AsyncResetSynchronizer(self.cd_tx, tx_reset_deglitched)
        ]

        # rx clocking
        rx_reset_deglitched = Signal()
        rx_reset_deglitched.attr.add("no_retiming")
        self.sync.tx += rx_reset_deglitched.eq(~rx_init.done)
        self.clock_domains.cd_rx = ClockDomain()
        self.specials += [
            Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rx.clk),
            AsyncResetSynchronizer(self.cd_rx, rx_reset_deglitched)
        ]

        # tx data and prbs
        self.submodules.tx_prbs = ClockDomainsRenamer("tx")(PRBSTX(20, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.comb += [
            self.tx_prbs.i.eq(Cat(*[self.encoder.output[i]
                                    for i in range(2)])),
            If(
                tx_produce_square_wave,
                # square wave @ linerate/20 for scope observation
                txdata.eq(0b11111111110000000000)).Else(
                    txdata.eq(self.tx_prbs.o))
        ]

        # rx data and prbs
        self.submodules.rx_prbs = ClockDomainsRenamer("rx")(PRBSRX(20, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        self.comb += [
            self.decoders[0].input.eq(rxdata[:10]),
            self.decoders[1].input.eq(rxdata[10:]),
            self.rx_prbs.i.eq(rxdata)
        ]

        # clock alignment
        if clock_aligner:
            clock_aligner = BruteforceClockAligner(0b0101111100,
                                                   self.tx_clk_freq,
                                                   check_period=10e-3)
            self.submodules += clock_aligner
            self.comb += [
                clock_aligner.rxdata.eq(rxdata),
                rx_init.restart.eq(clock_aligner.restart),
                self.rx_ready.eq(clock_aligner.ready)
            ]
        else:
            self.comb += self.rx_ready.eq(rx_init.done)
    def __init__(self, pll, pads, mode="master"):
        self.tx_pattern = CSRStorage(20)
        self.tx_produce_square_wave = CSRStorage()
        self.tx_prbs_config = CSRStorage(2)

        self.rx_pattern = CSRStatus(20)
        self.rx_prbs_config = CSRStorage(2)
        self.rx_prbs_errors = CSRStatus(32)

        self.rx_bitslip_value = CSRStorage(5)
        self.rx_delay_rst = CSR()
        self.rx_delay_inc = CSRStorage()
        self.rx_delay_ce = CSR()

        # # #

        self.submodules.encoder = ClockDomainsRenamer("serdes")(Encoder(
            2, True))
        self.decoders = [
            ClockDomainsRenamer("serdes")(Decoder(True)) for _ in range(2)
        ]
        self.submodules += self.decoders

        # clocking
        # master mode:
        # - linerate/10 pll refclk provided externally
        # - linerate/10 clock generated on clk_pads
        # slave mode:
        # - linerate/10 pll refclk provided by clk_pads
        self.clock_domains.cd_serdes = ClockDomain()
        self.clock_domains.cd_serdes_10x = ClockDomain()
        self.clock_domains.cd_serdes_2p5x = ClockDomain()
        self.comb += [
            self.cd_serdes.clk.eq(pll.serdes_clk),
            self.cd_serdes_10x.clk.eq(pll.serdes_10x_clk),
            self.cd_serdes_2p5x.clk.eq(pll.serdes_2p5x_clk)
        ]
        self.specials += [
            AsyncResetSynchronizer(self.cd_serdes, ~pll.lock),
            AsyncResetSynchronizer(self.cd_serdes_10x, ~pll.lock),
            AsyncResetSynchronizer(self.cd_serdes_2p5x, ~pll.lock)
        ]

        # control/status cdc
        tx_pattern = Signal(20)
        tx_produce_square_wave = Signal()
        tx_prbs_config = Signal(2)

        rx_pattern = Signal(20)
        rx_prbs_config = Signal(2)
        rx_prbs_errors = Signal(32)

        rx_bitslip_value = Signal(5)

        self.specials += [
            MultiReg(self.tx_pattern.storage, tx_pattern, "serdes"),
            MultiReg(self.tx_produce_square_wave.storage,
                     tx_produce_square_wave, "serdes"),
            MultiReg(self.tx_prbs_config.storage, tx_prbs_config, "serdes"),
        ]

        self.specials += [
            MultiReg(rx_pattern, self.rx_pattern.status, "sys"),
            MultiReg(self.rx_prbs_config.storage, rx_prbs_config, "serdes"),
            MultiReg(rx_prbs_errors, self.rx_prbs_errors.status,
                     "sys"),  # FIXME
        ]

        self.specials += MultiReg(self.rx_bitslip_value.storage,
                                  rx_bitslip_value, "serdes"),

        # tx clock (linerate/10)
        if mode == "master":
            self.submodules.tx_clk_gearbox = Gearbox(20, "serdes", 8,
                                                     "serdes_2p5x")
            self.comb += self.tx_clk_gearbox.i.eq(0b11111000001111100000)

            clk_o = Signal()
            self.specials += [
                Instance("OSERDESE2",
                         p_DATA_WIDTH=8,
                         p_TRISTATE_WIDTH=1,
                         p_DATA_RATE_OQ="DDR",
                         p_DATA_RATE_TQ="BUF",
                         p_SERDES_MODE="MASTER",
                         o_OQ=clk_o,
                         i_OCE=1,
                         i_RST=ResetSignal("serdes_2p5x"),
                         i_CLK=ClockSignal("serdes_10x"),
                         i_CLKDIV=ClockSignal("serdes_2p5x"),
                         i_D1=self.tx_clk_gearbox.o[0],
                         i_D2=self.tx_clk_gearbox.o[1],
                         i_D3=self.tx_clk_gearbox.o[2],
                         i_D4=self.tx_clk_gearbox.o[3],
                         i_D5=self.tx_clk_gearbox.o[4],
                         i_D6=self.tx_clk_gearbox.o[5],
                         i_D7=self.tx_clk_gearbox.o[6],
                         i_D8=self.tx_clk_gearbox.o[7]),
                Instance("OBUFDS", i_I=clk_o, o_O=pads.clk_p, o_OB=pads.clk_n)
            ]

        # tx data and prbs
        self.submodules.tx_prbs = ClockDomainsRenamer("serdes")(PRBSTX(
            20, True))
        self.comb += self.tx_prbs.config.eq(tx_prbs_config)
        self.submodules.tx_gearbox = Gearbox(20, "serdes", 8, "serdes_2p5x")
        self.sync.serdes += [
            self.tx_prbs.i.eq(Cat(*[self.encoder.output[i]
                                    for i in range(2)])),
            If(tx_pattern != 0, self.tx_gearbox.i.eq(tx_pattern)).Elif(
                tx_produce_square_wave,
                # square wave @ linerate/20 for scope observation
                self.tx_gearbox.i.eq(0b11111111110000000000)).Else(
                    self.tx_gearbox.i.eq(self.tx_prbs.o))
        ]

        serdes_o = Signal()
        self.specials += [
            Instance("OSERDESE2",
                     p_DATA_WIDTH=8,
                     p_TRISTATE_WIDTH=1,
                     p_DATA_RATE_OQ="DDR",
                     p_DATA_RATE_TQ="BUF",
                     p_SERDES_MODE="MASTER",
                     o_OQ=serdes_o,
                     i_OCE=1,
                     i_RST=ResetSignal("serdes_2p5x"),
                     i_CLK=ClockSignal("serdes_10x"),
                     i_CLKDIV=ClockSignal("serdes_2p5x"),
                     i_D1=self.tx_gearbox.o[0],
                     i_D2=self.tx_gearbox.o[1],
                     i_D3=self.tx_gearbox.o[2],
                     i_D4=self.tx_gearbox.o[3],
                     i_D5=self.tx_gearbox.o[4],
                     i_D6=self.tx_gearbox.o[5],
                     i_D7=self.tx_gearbox.o[6],
                     i_D8=self.tx_gearbox.o[7]),
            Instance("OBUFDS", i_I=serdes_o, o_O=pads.tx_p, o_OB=pads.tx_n)
        ]

        # rx clock
        use_bufr = False
        if mode == "slave":
            clk_i = Signal()

            clk_i_bufg = Signal()
            self.specials += [
                Instance("IBUFDS", i_I=pads.clk_p, i_IB=pads.clk_n, o_O=clk_i)
            ]
            if use_bufr:
                clk_i_bufr = Signal()
                self.specials += [
                    Instance("BUFR", i_I=clk_i, o_O=clk_i_bufr),
                    Instance("BUFG", i_I=clk_i_bufr, o_O=clk_i_bufg),
                ]
            else:
                self.specials += Instance("BUFG", i_I=clk_i, o_O=clk_i_bufg),
            self.comb += pll.refclk.eq(clk_i_bufg)

        # rx
        self.submodules.rx_gearbox = Gearbox(8, "serdes_2p5x", 20, "serdes")
        self.submodules.rx_bitslip = ClockDomainsRenamer("serdes")(BitSlip(20))

        self.submodules.phase_detector = ClockDomainsRenamer("serdes_2p5x")(
            PhaseDetector())

        # use 2 serdes for phase detection: 1 master/ 1 slave
        serdes_m_i_nodelay = Signal()
        serdes_s_i_nodelay = Signal()
        self.specials += [
            Instance(
                "IBUFDS_DIFF_OUT",
                i_I=pads.rx_p,
                i_IB=pads.rx_n,
                o_O=serdes_m_i_nodelay,
                o_OB=serdes_s_i_nodelay,
            )
        ]

        serdes_m_i_delayed = Signal()
        serdes_m_q = Signal(8)
        serdes_m_idelay_value = int(1 / (4 * pll.linerate) /
                                    78e-12)  # 1/4 bit period
        assert serdes_m_idelay_value < 32
        self.specials += [
            Instance("IDELAYE2",
                     p_DELAY_SRC="IDATAIN",
                     p_SIGNAL_PATTERN="DATA",
                     p_CINVCTRL_SEL="FALSE",
                     p_HIGH_PERFORMANCE_MODE="TRUE",
                     p_REFCLK_FREQUENCY=200.0,
                     p_PIPE_SEL="FALSE",
                     p_IDELAY_TYPE="VARIABLE",
                     p_IDELAY_VALUE=serdes_m_idelay_value,
                     i_C=ClockSignal(),
                     i_LD=self.rx_delay_rst.re,
                     i_CE=self.rx_delay_ce.re,
                     i_LDPIPEEN=0,
                     i_INC=self.rx_delay_inc.storage,
                     i_IDATAIN=serdes_m_i_nodelay,
                     o_DATAOUT=serdes_m_i_delayed),
            Instance("ISERDESE2",
                     p_DATA_WIDTH=8,
                     p_DATA_RATE="DDR",
                     p_SERDES_MODE="MASTER",
                     p_INTERFACE_TYPE="NETWORKING",
                     p_NUM_CE=1,
                     p_IOBDELAY="IFD",
                     i_DDLY=serdes_m_i_delayed,
                     i_CE1=1,
                     i_RST=ResetSignal("serdes_2p5x"),
                     i_CLK=ClockSignal("serdes_10x"),
                     i_CLKB=~ClockSignal("serdes_10x"),
                     i_CLKDIV=ClockSignal("serdes_2p5x"),
                     i_BITSLIP=0,
                     o_Q8=serdes_m_q[0],
                     o_Q7=serdes_m_q[1],
                     o_Q6=serdes_m_q[2],
                     o_Q5=serdes_m_q[3],
                     o_Q4=serdes_m_q[4],
                     o_Q3=serdes_m_q[5],
                     o_Q2=serdes_m_q[6],
                     o_Q1=serdes_m_q[7]),
        ]
        self.comb += self.phase_detector.mdata.eq(serdes_m_q)

        serdes_s_i_delayed = Signal()
        serdes_s_q = Signal(8)
        serdes_s_idelay_value = int(1 / (2 * pll.linerate) /
                                    78e-12)  # 1/2 bit period
        assert serdes_s_idelay_value < 32
        self.specials += [
            Instance("IDELAYE2",
                     p_DELAY_SRC="IDATAIN",
                     p_SIGNAL_PATTERN="DATA",
                     p_CINVCTRL_SEL="FALSE",
                     p_HIGH_PERFORMANCE_MODE="TRUE",
                     p_REFCLK_FREQUENCY=200.0,
                     p_PIPE_SEL="FALSE",
                     p_IDELAY_TYPE="VARIABLE",
                     p_IDELAY_VALUE=serdes_s_idelay_value,
                     i_C=ClockSignal(),
                     i_LD=self.rx_delay_rst.re,
                     i_CE=self.rx_delay_ce.re,
                     i_LDPIPEEN=0,
                     i_INC=self.rx_delay_inc.storage,
                     i_IDATAIN=serdes_s_i_nodelay,
                     o_DATAOUT=serdes_s_i_delayed),
            Instance("ISERDESE2",
                     p_DATA_WIDTH=8,
                     p_DATA_RATE="DDR",
                     p_SERDES_MODE="MASTER",
                     p_INTERFACE_TYPE="NETWORKING",
                     p_NUM_CE=1,
                     p_IOBDELAY="IFD",
                     i_DDLY=serdes_s_i_delayed,
                     i_CE1=1,
                     i_RST=ResetSignal("serdes_2p5x"),
                     i_CLK=ClockSignal("serdes_10x"),
                     i_CLKB=~ClockSignal("serdes_10x"),
                     i_CLKDIV=ClockSignal("serdes_2p5x"),
                     i_BITSLIP=0,
                     o_Q8=serdes_s_q[0],
                     o_Q7=serdes_s_q[1],
                     o_Q6=serdes_s_q[2],
                     o_Q5=serdes_s_q[3],
                     o_Q4=serdes_s_q[4],
                     o_Q3=serdes_s_q[5],
                     o_Q2=serdes_s_q[6],
                     o_Q1=serdes_s_q[7]),
        ]
        self.comb += self.phase_detector.sdata.eq(~serdes_s_q)

        # rx data and prbs
        self.submodules.rx_prbs = ClockDomainsRenamer("serdes")(PRBSRX(
            20, True))
        self.comb += [
            self.rx_prbs.config.eq(rx_prbs_config),
            rx_prbs_errors.eq(self.rx_prbs.errors)
        ]
        self.comb += [
            self.rx_gearbox.i.eq(serdes_m_q),
            self.rx_bitslip.value.eq(rx_bitslip_value),
            self.rx_bitslip.i.eq(self.rx_gearbox.o),
            rx_pattern.eq(self.rx_gearbox.o),
            self.decoders[0].input.eq(self.rx_bitslip.o[:10]),
            self.decoders[1].input.eq(self.rx_bitslip.o[10:]),
            self.rx_prbs.i.eq(self.rx_bitslip.o)
        ]