artiq/artiq/gateware/drtio/transceiver/gth_ultrascale.py

705 lines
33 KiB
Python

from functools import reduce
from operator import or_, and_
from migen import *
from migen.genlib.resetsync import AsyncResetSynchronizer
from misoc.cores.code_8b10b import Encoder, Decoder
from microscope import *
from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface
from artiq.gateware.drtio.transceiver.clock_aligner import BruteforceClockAligner
from artiq.gateware.drtio.transceiver.gth_ultrascale_init import *
class GTHSingle(Module):
def __init__(self, refclk, pads, sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode):
assert (dw == 20) or (dw == 40)
assert mode in ["single", "master", "slave"]
self.mode = mode
# phase alignment
self.txsyncallin = Signal()
self.txphaligndone = Signal()
self.txsyncallin = Signal()
self.txsyncin = Signal()
self.txsyncout = Signal()
self.txdlysreset = Signal()
# # #
self.txenable = Signal()
nwords = dw//10
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()
# transceiver direct clock outputs
# for OBUFDS_GTE3
self.rxrecclkout = Signal()
# 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
self.submodules.tx_init = tx_init = GTHInit(sys_clk_freq, False, mode)
# RX receives restart commands from RTIO domain
rx_init = ClockDomainsRenamer("rtio_tx")(GTHInit(rtio_clk_freq, True))
self.submodules += rx_init
cpll_reset = Signal()
cpll_lock = Signal()
self.comb += [
cpll_reset.eq(tx_init.pllreset),
tx_init.plllock.eq(cpll_lock),
rx_init.plllock.eq(cpll_lock)
]
txdata = Signal(dw)
rxdata = Signal(dw)
rxphaligndone = Signal()
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 =20,
p_CLK_COR_MIN_LAT =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 =5,
p_CPLL_FBDIV_45 =4,
p_CPLL_INIT_CFG0 =0b0000001010110010,
p_CPLL_INIT_CFG1 =0b00000000,
p_CPLL_LOCK_CFG =0b0000000111101000,
p_CPLL_REFCLK_DIV =1,
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,
)
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 ="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 =0,
p_RXBUF_THRESH_OVRD ="FALSE",
p_RXBUF_THRESH_UNDFLW =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 =2,
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 =6,
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 =dw,
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 =dw==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 ="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 ="TRUE",
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,
)
gth_params.update(
p_TXBUF_EN ="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 =2,
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 if mode == "single" else 1,
p_TXSYNC_OVRD =0b0,
p_TXSYNC_SKIP_DA =0b0,
p_TX_CLK25_DIV =6,
p_TX_CLKMUX_EN =0b1,
p_TX_DATA_WIDTH =dw,
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 =dw==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 =dw/rtiox_mul,
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 ="TXUSR",
p_USE_PCS_CLK_PHASE_SEL =0b0,
p_WB_MODE =0b00,
)
gth_params.update(
# Reset modes
i_GTRESETSEL=0,
i_RESETOVRD=0,
i_CPLLRESET=0,
i_CPLLPD=cpll_reset,
o_CPLLLOCK=cpll_lock,
i_CPLLLOCKEN=1,
i_CPLLREFCLKSEL=0b001,
i_TSTIN=2**20-1,
i_GTREFCLK0=refclk,
# TX clock
o_TXOUTCLK=self.txoutclk,
i_TXSYSCLKSEL=0b00,
i_TXPLLCLKSEL=0b00,
i_TXOUTCLKSEL=0b101,
# TX Startup/Reset
i_GTTXRESET=tx_init.gtXxreset,
o_TXRESETDONE=tx_init.Xxresetdone,
i_TXDLYSRESET=tx_init.Xxdlysreset if mode != "slave" else self.txdlysreset,
o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone,
o_TXPHALIGNDONE=tx_init.Xxphaligndone,
i_TXUSERRDY=tx_init.Xxuserrdy,
i_TXSYNCMODE=mode != "slave",
i_TXSYNCALLIN=self.txsyncallin,
i_TXSYNCIN=self.txsyncin,
o_TXSYNCOUT=self.txsyncout,
# TX data
i_TXINHIBIT=~self.txenable,
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,
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,
i_RXSYSCLKSEL=0b00,
i_RXOUTCLKSEL=0b010,
i_RXPLLCLKSEL=0b00,
o_RXRECCLKOUT=self.rxrecclkout,
o_RXOUTCLK=self.rxoutclk,
i_RXUSRCLK=ClockSignal("rtio_rx"),
i_RXUSRCLK2=ClockSignal("rtio_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=Replicate(rx_init.restart, 2),
i_RXELECIDLEMODE=0b11,
# Pads
i_GTHRXP=pads.rxp,
i_GTHRXN=pads.rxn,
o_GTHTXP=pads.txp,
o_GTHTXN=pads.txn
)
self.specials += Instance("GTHE3_CHANNEL", **gth_params)
self.comb += self.txphaligndone.eq(tx_init.Xxphaligndone)
self.submodules += [
add_probe_async("drtio_gth", "cpll_lock", cpll_lock),
add_probe_async("drtio_gth", "txuserrdy", tx_init.Xxuserrdy),
add_probe_async("drtio_gth", "tx_init_done", tx_init.done),
add_probe_async("drtio_gth", "rxuserrdy", rx_init.Xxuserrdy),
add_probe_async("drtio_gth", "rx_init_done", rx_init.done),
add_probe_buffer("drtio_gth", "txdata", txdata, clock_domain="rtio_tx"),
add_probe_buffer("drtio_gth", "rxdata", rxdata, clock_domain="rtio_rx")
]
# 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()
self.clock_domains.cd_rtiox_tx = ClockDomain()
if mode == "master" or mode == "single":
self.specials += [
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtiox_tx.clk, i_DIV=0),
Instance("BUFG_GT", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk, i_DIV=rtiox_mul-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, 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)
]
self.submodules += add_probe_async("drtio_gth", "clock_aligner_ready", clock_aligner.ready)
class GTHTXPhaseAlignement(Module):
# TX Buffer Bypass in Single-Lane/Multi-Lane Auto Mode (ug576)
def __init__(self, gths):
txsyncallin = Signal()
txsync = Signal()
txphaligndone = Signal(len(gths))
txdlysreset = Signal()
ready_for_align = Signal(len(gths))
all_ready_for_align = Signal()
for i, gth in enumerate(gths):
# Common to all transceivers
self.comb += [
ready_for_align[i].eq(1),
gth.txsyncin.eq(txsync),
gth.txsyncallin.eq(txsyncallin),
txphaligndone[i].eq(gth.txphaligndone)
]
# Specific to Master or Single transceivers
if gth.mode == "master" or gth.mode == "single":
self.comb += [
gth.tx_init.all_ready_for_align.eq(all_ready_for_align),
txsync.eq(gth.txsyncout),
txdlysreset.eq(gth.tx_init.Xxdlysreset)
]
# Specific to Slave transceivers
else:
self.comb += [
ready_for_align[i].eq(gth.tx_init.ready_for_align),
gth.txdlysreset.eq(txdlysreset),
]
self.comb += [
txsyncallin.eq(reduce(and_, [txphaligndone[i] for i in range(len(gths))])),
all_ready_for_align.eq(reduce(and_, [ready_for_align[i] for i in range(len(gths))]))
]
class GTH(Module, TransceiverInterface):
def __init__(self, clock_pads, data_pads, sys_clk_freq, rtio_clk_freq, rtiox_mul=2, dw=20, master=0, clock_recout_pads=None):
self.nchannels = nchannels = len(data_pads)
self.gths = []
# # #
create_buf = hasattr(clock_pads, "p")
if create_buf:
refclk = Signal()
ibufds_ceb = Signal()
self.specials += Instance("IBUFDS_GTE3",
i_CEB=ibufds_ceb,
i_I=clock_pads.p,
i_IB=clock_pads.n,
o_O=refclk)
else:
refclk = clock_pads
rtio_tx_clk = Signal()
channel_interfaces = []
for i in range(nchannels):
if nchannels == 1:
mode = "single"
else:
mode = "master" if i == master else "slave"
gth = GTHSingle(refclk, data_pads[i], sys_clk_freq, rtio_clk_freq, rtiox_mul, dw, mode)
if mode == "master":
self.comb += rtio_tx_clk.eq(gth.cd_rtio_tx.clk)
elif mode == "slave":
self.comb += gth.cd_rtio_tx.clk.eq(rtio_tx_clk)
self.gths.append(gth)
setattr(self.submodules, "gth"+str(i), gth)
channel_interface = ChannelInterface(gth.encoder, gth.decoders)
self.comb += channel_interface.rx_ready.eq(gth.rx_ready)
channel_interfaces.append(channel_interface)
self.submodules.tx_phase_alignment = GTHTXPhaseAlignement(self.gths)
TransceiverInterface.__init__(self, channel_interfaces)
for n, gth in enumerate(self.gths):
self.comb += gth.txenable.eq(self.txenable.storage[n])
self.clock_domains.cd_rtiox = ClockDomain(reset_less=True)
if create_buf:
# GTH PLLs recover on their own from an interrupted clock input,
# but be paranoid about HMC7043 noise.
self.stable_clkin.storage.attr.add("no_retiming")
self.comb += ibufds_ceb.eq(~self.stable_clkin.storage)
self.comb += [
self.cd_rtio.clk.eq(self.gths[master].cd_rtio_tx.clk),
self.cd_rtiox.clk.eq(self.gths[master].cd_rtiox_tx.clk),
self.cd_rtio.rst.eq(reduce(or_, [gth.cd_rtio_tx.rst for gth in self.gths]))
]
for i in range(nchannels):
self.comb += [
getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gths[i].cd_rtio_rx.clk),
getattr(self, "cd_rtio_rx" + str(i)).rst.eq(self.gths[i].cd_rtio_rx.rst)
]
if clock_recout_pads is not None:
self.specials += Instance("OBUFDS_GTE3",
p_REFCLK_EN_TX_PATH=0b1,
p_REFCLK_ICNTL_TX=0b00111,
i_I=self.gths[0].rxrecclkout,
i_CEB=0,
o_O=clock_recout_pads.p, o_OB=clock_recout_pads.n)