diff --git a/artiq/firmware/runtime/rtio_clocking.rs b/artiq/firmware/runtime/rtio_clocking.rs index 3b230bacc..b6cf5f395 100644 --- a/artiq/firmware/runtime/rtio_clocking.rs +++ b/artiq/firmware/runtime/rtio_clocking.rs @@ -153,6 +153,8 @@ fn setup_si5324_as_synthesizer() { let si5324_ref_input = si5324::Input::Ckin2; #[cfg(soc_platform = "metlino")] let si5324_ref_input = si5324::Input::Ckin2; + #[cfg(soc_platform = "kc705")] + let si5324_ref_input = si5324::Input::Ckin2; si5324::setup(&SI5324_SETTINGS, si5324_ref_input).expect("cannot initialize Si5324"); } @@ -165,6 +167,8 @@ pub fn init() { let si5324_ext_input = si5324::Input::Ckin2; #[cfg(soc_platform = "metlino")] let si5324_ext_input = si5324::Input::Ckin2; + #[cfg(soc_platform = "kc705")] + let si5324_ext_input = si5324::Input::Ckin2; match get_rtio_clock_cfg() { RtioClock::Internal => setup_si5324_as_synthesizer(), RtioClock::External => si5324::bypass(si5324_ext_input).expect("cannot bypass Si5324") diff --git a/artiq/gateware/drtio/transceiver/gtx_7series.py b/artiq/gateware/drtio/transceiver/gtx_7series.py new file mode 100644 index 000000000..a2da39822 --- /dev/null +++ b/artiq/gateware/drtio/transceiver/gtx_7series.py @@ -0,0 +1,341 @@ +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from misoc.cores.code_8b10b import Encoder, Decoder +from misoc.interconnect.csr import * + +from artiq.gateware.drtio.core import TransceiverInterface, ChannelInterface +from artiq.gateware.drtio.transceiver.clock_aligner import BruteforceClockAligner +from artiq.gateware.drtio.transceiver.gtx_7series_init import * + + +class GTX_20X(Module): + # Settings: + # * GTX reference clock @ 125MHz == coarse RTIO frequency + # * GTX data width = 20 + # * GTX PLL frequency @ 2.5GHz + # * GTX line rate (TX & RX) @ 2.5Gb/s + # * GTX TX/RX USRCLK @ 125MHz == coarse RTIO frequency + def __init__(self, refclk, tx_pads, rx_pads, sys_clk_freq, rtio_clk_freq=125e6, tx_mode="single", rx_mode="single"): + assert tx_mode in ["single", "master", "slave"] + assert rx_mode in ["single", "master", "slave"] + + self.txenable = Signal() + self.submodules.encoder = ClockDomainsRenamer("rtio_tx")( + Encoder(2, True)) + self.submodules.decoders = [ClockDomainsRenamer("rtio_rx")( + (Decoder(True))) for _ in range(2)] + 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() + + # # # + + cpllreset = Signal() + cplllock = Signal() + # TX generates RTIO clock, init must be in system domain + self.submodules.tx_init = tx_init = GTXInit(sys_clk_freq, False, mode=tx_mode) + # RX receives restart commands from RTIO domain + self.submodules.rx_init = rx_init = ClockDomainsRenamer("rtio_tx")( + GTXInit(rtio_clk_freq, True, mode=rx_mode)) + self.comb += [ + cpllreset.eq(tx_init.cpllreset), + tx_init.cplllock.eq(cplllock), + rx_init.cplllock.eq(cplllock) + ] + + txdata = Signal(20) + rxdata = Signal(20) + # Note: the following parameters were set after consulting AR45360 + self.specials += \ + Instance("GTXE2_CHANNEL", + # PMA Attributes + p_PMA_RSV=0x00018480, + p_PMA_RSV2=0x2050, # PMA_RSV2[5] = 0: Eye scan feature disabled + p_PMA_RSV3=0, + p_PMA_RSV4=1, # PMA_RSV[4],RX_CM_TRIM[2:0] = 0b1010: Common mode 800mV + p_RX_BIAS_CFG=0b000000000100, + p_RX_OS_CFG=0b0000010000000, + 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=4, + p_CPLL_FBDIV_45=5, + p_CPLL_REFCLK_DIV=1, + p_RXOUT_DIV=2, + p_TXOUT_DIV=2, + i_CPLLRESET=cpllreset, + i_CPLLPD=cpllreset, + o_CPLLLOCK=cplllock, + i_CPLLLOCKEN=1, + i_CPLLREFCLKSEL=0b001, + i_TSTIN=2**20-1, + i_GTREFCLK0=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_TXPHDLYRESET=0, + i_TXDLYBYPASS=0, + i_TXPHALIGNEN=1 if tx_mode != "single" else 0, + i_GTTXRESET=tx_init.gtXxreset, + o_TXRESETDONE=tx_init.Xxresetdone, + i_TXDLYSRESET=tx_init.Xxdlysreset, + o_TXDLYSRESETDONE=tx_init.Xxdlysresetdone, + i_TXPHINIT=tx_init.txphinit if tx_mode != "single" else 0, + o_TXPHINITDONE=tx_init.txphinitdone if tx_mode != "single" else Signal(), + i_TXPHALIGN=tx_init.Xxphalign if tx_mode != "single" else 0, + i_TXDLYEN=tx_init.Xxdlyen if tx_mode != "single" else 0, + o_TXPHALIGNDONE=tx_init.Xxphaligndone, + i_TXUSERRDY=tx_init.Xxuserrdy, + p_TXPMARESET_TIME=1, + p_TXPCSRESET_TIME=1, + i_TXINHIBIT=~self.txenable, + + # 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("rtio_tx"), + i_TXUSRCLK2=ClockSignal("rtio_tx"), + + # TX electrical + i_TXBUFDIFFCTRL=0b100, + i_TXDIFFCTRL=0b1000, + + # RX Startup/Reset + i_RXPHDLYRESET=0, + i_RXDLYBYPASS=0, + i_RXPHALIGNEN=1 if rx_mode != "single" else 0, + i_GTRXRESET=rx_init.gtXxreset, + o_RXRESETDONE=rx_init.Xxresetdone, + i_RXDLYSRESET=rx_init.Xxdlysreset, + o_RXDLYSRESETDONE=rx_init.Xxdlysresetdone, + i_RXPHALIGN=rx_init.Xxphalign if rx_mode != "single" else 0, + i_RXDLYEN=rx_init.Xxdlyen if rx_mode != "single" else 0, + o_RXPHALIGNDONE=rx_init.Xxphaligndone, + i_RXUSERRDY=rx_init.Xxuserrdy, + p_RXPMARESET_TIME=1, + p_RXPCSRESET_TIME=1, + + # RX AFE + p_RX_DFE_XYD_CFG=0, + p_RX_CM_SEL=0b11, # RX_CM_SEL = 0b11: Common mode is programmable + p_RX_CM_TRIM=0b010, # PMA_RSV[4],RX_CM_TRIM[2:0] = 0b1010: Common mode 800mV + i_RXDFEXYDEN=1, + i_RXDFEXYDHOLD=0, + i_RXDFEXYDOVRDEN=0, + i_RXLPMEN=0, # RXLPMEN = 0: DFE mode is enabled + p_RX_DFE_GAIN_CFG=0x0207EA, + p_RX_DFE_VP_CFG=0b00011111100000011, + p_RX_DFE_UT_CFG=0b10001000000000000, + p_RX_DFE_KL_CFG=0b0000011111110, + p_RX_DFE_KL_CFG2=0x3788140A, + p_RX_DFE_H2_CFG=0b000110000000, + p_RX_DFE_H3_CFG=0b000110000000, + p_RX_DFE_H4_CFG=0b00011100000, + p_RX_DFE_H5_CFG=0b00011100000, + p_RX_DFE_LPM_CFG=0x0904, # RX_DFE_LPM_CFG = 0x0904: linerate <= 6.6Gb/s + # = 0x0104: linerate > 6.6Gb/s + + # RX clock + i_RXDDIEN=1, + i_RXSYSCLKSEL=0b00, + i_RXOUTCLKSEL=0b010, + 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=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]), + + # RX Byte and Word Alignment Attributes + p_ALIGN_COMMA_DOUBLE="FALSE", + p_ALIGN_COMMA_ENABLE=0b1111111111, + p_ALIGN_COMMA_WORD=1, + p_ALIGN_MCOMMA_DET="TRUE", + p_ALIGN_MCOMMA_VALUE=0b1010000011, + p_ALIGN_PCOMMA_DET="TRUE", + p_ALIGN_PCOMMA_VALUE=0b0101111100, + p_SHOW_REALIGN_COMMA="FALSE", + p_RXSLIDE_AUTO_WAIT=7, + p_RXSLIDE_MODE="PCS", + p_RX_SIG_VALID_DLY=10, + + # RX 8B/10B Decoder Attributes + p_RX_DISPERR_SEQ_MATCH="FALSE", + p_DEC_MCOMMA_DETECT="TRUE", + p_DEC_PCOMMA_DETECT="TRUE", + p_DEC_VALID_COMMA_ONLY="FALSE", + + # RX Buffer Attributes + p_RXBUF_ADDR_MODE="FAST", + p_RXBUF_EIDLE_HI_CNT=0b1000, + p_RXBUF_EIDLE_LO_CNT=0b0000, + p_RXBUF_EN="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", # RXBUF_RESET_ON_EIDLE = FALSE: OOB is disabled + 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="RXUSR", + p_RX_DDI_SEL=0b000000, + p_RX_DEFER_RESET_BUF_EN="TRUE", + + # CDR Attributes + p_RXCDR_CFG=0x03000023FF20400020, # DFE @ <= 6.6Gb/s, scrambled, CDR setting < +/- 200ppm + # (See UG476 (v1.12.1), p.206) + 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, + + # Pads + i_GTXRXP=rx_pads.p, + i_GTXRXN=rx_pads.n, + o_GTXTXP=tx_pads.p, + o_GTXTXN=tx_pads.n, + + # Other parameters + p_PCS_RSVD_ATTR=( + (tx_mode != "single") << 1 | # PCS_RSVD_ATTR[1] = 0: TX Single Lane Auto Mode + # = 1: TX Manual Mode + (rx_mode != "single") << 2 | # [2] = 0: RX Single Lane Auto Mode + # = 1: RX Manual Mode + 0 << 8 # [8] = 0: OOB is disabled + ), + i_RXELECIDLEMODE=0b11, # RXELECIDLEMODE = 0b11: OOB is disabled + p_RX_DFE_LPM_HOLD_DURING_EIDLE=0b0, + p_ES_EYE_SCAN_EN="TRUE", # Must be TRUE for GTX + ) + + # 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 tx_mode == "single" or tx_mode == "master": + self.specials += Instance("BUFG", i_I=self.txoutclk, o_O=self.cd_rtio_tx.clk) + 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 += rx_reset_deglitched.eq(~rx_init.done) + self.clock_domains.cd_rtio_rx = ClockDomain() + if rx_mode == "single" or rx_mode == "master": + self.specials += Instance("BUFG", i_I=self.rxoutclk, o_O=self.cd_rtio_rx.clk), + self.specials += AsyncResetSynchronizer(self.cd_rtio_rx, rx_reset_deglitched) + + self.comb += [ + txdata.eq(Cat(self.encoder.output[0], self.encoder.output[1])), + self.decoders[0].input.eq(rxdata[:10]), + self.decoders[1].input.eq(rxdata[10:]) + ] + + 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) + ] + + + +class GTX(Module, TransceiverInterface): + def __init__(self, clock_pads, tx_pads, rx_pads, sys_clk_freq, rtio_clk_freq=125e6, master=0): + assert len(tx_pads) == len(rx_pads) + self.nchannels = nchannels = len(tx_pads) + self.gtxs = [] + self.rtio_clk_freq = rtio_clk_freq + + # # # + + refclk = Signal() + stable_clkin_n = Signal() + self.specials += Instance("IBUFDS_GTE2", + i_CEB=stable_clkin_n, + i_I=clock_pads.p, + i_IB=clock_pads.n, + o_O=refclk + ) + + rtio_tx_clk = Signal() + channel_interfaces = [] + for i in range(nchannels): + if nchannels == 1: + mode = "single" + else: + mode = "master" if i == master else "slave" + # Note: RX phase alignment is to be done on individual lanes, not multi-lane. + gtx = GTX_20X(refclk, tx_pads[i], rx_pads[i], sys_clk_freq, rtio_clk_freq=rtio_clk_freq, tx_mode=mode, rx_mode="single") + # Fan-out (to slave) / Fan-in (from master) of the TXUSRCLK + if mode == "slave": + self.comb += gtx.cd_rtio_tx.clk.eq(rtio_tx_clk) + else: + self.comb += rtio_tx_clk.eq(gtx.cd_rtio_tx.clk) + self.gtxs.append(gtx) + setattr(self.submodules, "gtx"+str(i), gtx) + channel_interface = ChannelInterface(gtx.encoder, gtx.decoders) + self.comb += channel_interface.rx_ready.eq(gtx.rx_ready) + channel_interfaces.append(channel_interface) + + self.submodules.tx_phase_alignment = GTXInitPhaseAlignment([gtx.tx_init for gtx in self.gtxs]) + + TransceiverInterface.__init__(self, channel_interfaces) + for n, gtx in enumerate(self.gtxs): + self.comb += [ + stable_clkin_n.eq(~self.stable_clkin.storage), + gtx.txenable.eq(self.txenable.storage[n]) + ] + + # Connect master's `rtio_tx` clock to `rtio` clock + self.comb += [ + self.cd_rtio.clk.eq(self.gtxs[master].cd_rtio_tx.clk), + self.cd_rtio.rst.eq(reduce(or_, [gtx.cd_rtio_tx.rst for gtx in self.gtxs])) + ] + # Connect slave i's `rtio_rx` clock to `rtio_rxi` clock + for i in range(nchannels): + self.comb += [ + getattr(self, "cd_rtio_rx" + str(i)).clk.eq(self.gtxs[i].cd_rtio_rx.clk), + getattr(self, "cd_rtio_rx" + str(i)).rst.eq(self.gtxs[i].cd_rtio_rx.rst) + ] diff --git a/artiq/gateware/drtio/transceiver/gtx_7series_init.py b/artiq/gateware/drtio/transceiver/gtx_7series_init.py new file mode 100644 index 000000000..70c69a19c --- /dev/null +++ b/artiq/gateware/drtio/transceiver/gtx_7series_init.py @@ -0,0 +1,250 @@ +from math import ceil + +from migen import * +from migen.genlib.cdc import MultiReg +from migen.genlib.misc import WaitTimer +from migen.genlib.fsm import FSM + + +class GTXInit(Module): + # Based on LiteSATA by Enjoy-Digital + # Choose between Auto Mode and Manual Mode for TX/RX phase alignment with buffer bypassed: + # * Auto Mode: When only single lane is involved, as suggested by Xilinx (AR59612) + # * Manual Mode: When only multi-lane is involved, as suggested by Xilinx (AR59612) + def __init__(self, sys_clk_freq, rx, mode="single"): + assert isinstance(rx, bool) + assert mode in ["single", "master", "slave"] + self.mode = mode + + self.done = Signal() + self.restart = Signal() + + # GTX signals + self.cplllock = Signal() + self.cpllreset = Signal() + self.gtXxreset = Signal() + self.Xxresetdone = Signal() + self.Xxdlysreset = Signal() + self.Xxdlysresetdone = Signal() + self.Xxphaligndone = Signal() + self.Xxuserrdy = Signal() + # GTX signals exclusive to multi-lane + if mode != "single": + self.Xxphalign = Signal() + self.Xxdlyen = Signal() + # TX only: + if not rx: + self.txphinit = Signal() + self.txphinitdone = Signal() + + # Strobe from master channel to initialize TX/RX phase alignment on slaves + self.master_phaligndone = Signal() + # Strobe from slave channels to re-enable TX/RX delay alignment on master; + # To be combinatorially AND-ed from all slave's `done` + if mode == "master": + self.slaves_phaligndone = Signal() + + # # # + + # Double-latch transceiver asynch outputs + cplllock = Signal() + Xxresetdone = Signal() + Xxdlysresetdone = Signal() + Xxphaligndone = Signal() + self.specials += [ + MultiReg(self.cplllock, cplllock), + MultiReg(self.Xxresetdone, Xxresetdone), + MultiReg(self.Xxdlysresetdone, Xxdlysresetdone), + MultiReg(self.Xxphaligndone, Xxphaligndone), + ] + if mode != "single": + txphinitdone = Signal() + self.specials += MultiReg(self.txphinitdone, txphinitdone) + + # Deglitch FSM outputs driving transceiver asynch inputs + gtXxreset = Signal() + Xxdlysreset = Signal() + Xxuserrdy = Signal() + self.sync += [ + self.gtXxreset.eq(gtXxreset), + self.Xxdlysreset.eq(Xxdlysreset), + self.Xxuserrdy.eq(Xxuserrdy) + ] + if mode != "single": + Xxphalign = Signal() + Xxdlyen = Signal() + self.sync += [ + self.Xxphalign.eq(Xxphalign), + self.Xxdlyen.eq(Xxdlyen) + ] + if not rx: + txphinit = Signal() + self.sync += self.txphinit.eq(txphinit) + + # After configuration, transceiver resets have to stay low for + # at least 500ns (see AR43482) + startup_cycles = ceil(500*sys_clk_freq/1000000000) + startup_timer = WaitTimer(startup_cycles) + self.submodules += startup_timer + + # PLL reset should be 1 period of refclk + # (i.e. 1/(125MHz) for the case of RTIO @ 125MHz) + pll_reset_cycles = ceil(sys_clk_freq/125e6) + pll_reset_timer = WaitTimer(pll_reset_cycles) + self.submodules += pll_reset_timer + + startup_fsm = FSM(reset_state="INITIAL") + self.submodules += startup_fsm + + if rx: + cdr_stable_timer = WaitTimer(1024) + self.submodules += cdr_stable_timer + + # Rising edge detection for phase alignment "done" + Xxphaligndone_r = Signal(reset=1) + Xxphaligndone_rising = Signal() + self.sync += Xxphaligndone_r.eq(Xxphaligndone) + self.comb += Xxphaligndone_rising.eq(Xxphaligndone & ~Xxphaligndone_r) + + startup_fsm.act("INITIAL", + startup_timer.wait.eq(1), + If(startup_timer.done, NextState("RESET_ALL")) + ) + startup_fsm.act("RESET_ALL", + gtXxreset.eq(1), + self.cpllreset.eq(1), + pll_reset_timer.wait.eq(1), + If(pll_reset_timer.done, NextState("RELEASE_PLL_RESET")) + ) + startup_fsm.act("RELEASE_PLL_RESET", + gtXxreset.eq(1), + If(cplllock, NextState("RELEASE_GTH_RESET")) + ) + # Release GTX reset and wait for GTX resetdone + # (from UG476, GTX is reset on falling edge + # of gttxreset) + if rx: + startup_fsm.act("RELEASE_GTH_RESET", + Xxuserrdy.eq(1), + cdr_stable_timer.wait.eq(1), + If(Xxresetdone & cdr_stable_timer.done, NextState("DELAY_ALIGN")) + ) + else: + startup_fsm.act("RELEASE_GTH_RESET", + Xxuserrdy.eq(1), + If(Xxresetdone, NextState("DELAY_ALIGN")) + ) + + # States exclusive to Auto Mode: + if mode == "single": + # Start delay alignment (pulse) + startup_fsm.act("DELAY_ALIGN", + Xxuserrdy.eq(1), + Xxdlysreset.eq(1), + NextState("WAIT_DELAY_ALIGN") + ) + # Wait for delay alignment + startup_fsm.act("WAIT_DELAY_ALIGN", + Xxuserrdy.eq(1), + If(Xxdlysresetdone, NextState("WAIT_FIRST_PHASE_ALIGN_DONE")) + ) + # Wait 2 rising edges of rxphaligndone + # (from UG476 in buffer bypass config) + startup_fsm.act("WAIT_FIRST_PHASE_ALIGN_DONE", + Xxuserrdy.eq(1), + If(Xxphaligndone_rising, NextState("WAIT_SECOND_PHASE_ALIGN_DONE")) + ) + startup_fsm.act("WAIT_SECOND_PHASE_ALIGN_DONE", + Xxuserrdy.eq(1), + If(Xxphaligndone_rising, NextState("READY")) + ) + + # States exclusive to Manual Mode: + else: + # Start delay alignment (hold) + startup_fsm.act("DELAY_ALIGN", + Xxuserrdy.eq(1), + Xxdlysreset.eq(1), + If(Xxdlysresetdone, + # TX master: proceed to initialize phase alignment manually + (NextState("PHASE_ALIGN_INIT") if not rx else + # RX master: proceed to start phase alignment manually + NextState("PHASE_ALIGN")) if mode == "master" else + # TX/RX slave: wait for phase alignment "done" on master + NextState("WAIT_MASTER") + ) + ) + if mode == "slave": + # TX slave: Wait for phase alignment "done" on master + startup_fsm.act("WAIT_MASTER", + Xxuserrdy.eq(1), + If(self.master_phaligndone, + # TX slave: proceed to initialize phase alignment manually + NextState("PHASE_ALIGN_INIT") if not rx else + # RX slave: proceed to start phase alignment manually + NextState("PHASE_ALIGN") + ) + ) + if not rx: + # TX master/slave: Initialize phase alignment, wait rising edge on "done" + startup_fsm.act("PHASE_ALIGN_INIT", + Xxuserrdy.eq(1), + txphinit.eq(1), + If(txphinitdone, NextState("PHASE_ALIGN")) + ) + # Do phase ealignment, wait rising edge on "done" + startup_fsm.act("PHASE_ALIGN", + Xxuserrdy.eq(1), + Xxphalign.eq(1), + If(Xxphaligndone_rising, + # TX/RX master: proceed to set T/RXDLYEN + NextState("FIRST_DLYEN") if mode == "master" else + # TX/RX slave: proceed to signal master + NextState("READY") + ) + ) + if mode == "master": + # Enable delay alignment in manual mode, wait rising edge on phase alignment "done" + startup_fsm.act("FIRST_DLYEN", + Xxuserrdy.eq(1), + Xxdlyen.eq(1), + If(Xxphaligndone_rising, NextState("WAIT_SLAVES")) + ) + # Wait for phase alignment "done" on all slaves + startup_fsm.act("WAIT_SLAVES", + Xxuserrdy.eq(1), + self.master_phaligndone.eq(1), + If(self.slaves_phaligndone, NextState("SECOND_DLYEN")) + ) + # Re-enable delay alignment in manual mode, wait rising edge on phase alignment "done" + startup_fsm.act("SECOND_DLYEN", + Xxuserrdy.eq(1), + Xxdlyen.eq(1), + If(Xxphaligndone_rising, NextState("READY")) + ) + + # Transceiver is ready, alignment can be restarted + startup_fsm.act("READY", + Xxuserrdy.eq(1), + self.done.eq(1), + If(self.restart, NextState("RESET_ALL")) + ) + + +class GTXInitPhaseAlignment(Module): + # Interconnect of phase alignment "done" signals for Manual Mode multi-lane + def __init__(self, gtx_inits): + master_phaligndone = Signal() # Fan-out to `slave.master_phaligndone`s + slaves_phaligndone = Signal(reset=1) # ANDed from `slave.done`s + # Slave channels + for gtx_init in gtx_inits: + if gtx_init.mode == "slave": + self.comb += gtx_init.master_phaligndone.eq(master_phaligndone) + slaves_phaligndone = slaves_phaligndone & gtx_init.done + # Master channels + for gtx_init in gtx_inits: + if gtx_init.mode == "master": + self.comb += [ + master_phaligndone.eq(gtx_init.master_phaligndone), + gtx_init.slaves_phaligndone.eq(slaves_phaligndone) + ] diff --git a/artiq/gateware/targets/kc705_drtio_master.py b/artiq/gateware/targets/kc705_drtio_master.py new file mode 100755 index 000000000..78d5d054b --- /dev/null +++ b/artiq/gateware/targets/kc705_drtio_master.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 + +import argparse + +from migen import * +from migen.build.generic_platform import * +from migen.build.xilinx.vivado import XilinxVivadoToolchain +from migen.build.xilinx.ise import XilinxISEToolchain + +from misoc.cores import spi as spi_csr +from misoc.cores import gpio +from misoc.integration.builder import * +from misoc.targets.kc705 import MiniSoC, soc_kc705_args, soc_kc705_argdict + +from artiq.gateware.amp import AMPSoC +from artiq.gateware import rtio +from artiq.gateware.rtio.phy import ttl_simple +from artiq.gateware.drtio.transceiver import gtx_7series +from artiq.gateware.drtio import * +from artiq.build_soc import * + + +class Master(MiniSoC, AMPSoC): + mem_map = { + "cri_con": 0x10000000, + "rtio": 0x20000000, + "rtio_dma": 0x30000000, + "drtioaux": 0x50000000, + "mailbox": 0x70000000 + } + mem_map.update(MiniSoC.mem_map) + + def __init__(self, gateware_identifier_str=None, **kwargs): + MiniSoC.__init__(self, + cpu_type="or1k", + sdram_controller_type="minicon", + l2_size=128*1024, + integrated_sram_size=8192, + ethmac_nrxslots=4, + ethmac_ntxslots=4, + **kwargs) + AMPSoC.__init__(self) + add_identifier(self, gateware_identifier_str=gateware_identifier_str) + + if isinstance(self.platform.toolchain, XilinxVivadoToolchain): + self.platform.toolchain.bitstream_commands.extend([ + "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", + ]) + if isinstance(self.platform.toolchain, XilinxISEToolchain): + self.platform.toolchain.bitgen_opt += " -g compress" + + platform = self.platform + + self.comb += platform.request("sfp_tx_disable_n").eq(1) + tx_pads = [ + platform.request("sfp_tx"), platform.request("user_sma_mgt_tx") + ] + rx_pads = [ + platform.request("sfp_rx"), platform.request("user_sma_mgt_rx") + ] + + # 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock + self.submodules.drtio_transceiver = gtx_7series.GTX( + clock_pads=platform.request("si5324_clkout"), + tx_pads=tx_pads, + rx_pads=rx_pads, + sys_clk_freq=self.clk_freq) + self.csr_devices.append("drtio_transceiver") + + self.submodules.rtio_tsc = rtio.TSC("async", glbl_fine_ts_width=3) + + drtio_csr_group = [] + drtioaux_csr_group = [] + drtioaux_memory_group = [] + drtio_cri = [] + for i in range(len(self.drtio_transceiver.channels)): + core_name = "drtio" + str(i) + coreaux_name = "drtioaux" + str(i) + memory_name = "drtioaux" + str(i) + "_mem" + drtio_csr_group.append(core_name) + drtioaux_csr_group.append(coreaux_name) + drtioaux_memory_group.append(memory_name) + + cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)}) + + core = cdr(DRTIOMaster( + self.rtio_tsc, self.drtio_transceiver.channels[i])) + setattr(self.submodules, core_name, core) + drtio_cri.append(core.cri) + self.csr_devices.append(core_name) + + coreaux = cdr(DRTIOAuxController(core.link_layer)) + setattr(self.submodules, coreaux_name, coreaux) + self.csr_devices.append(coreaux_name) + + memory_address = self.mem_map["drtioaux"] + 0x800*i + self.add_wb_slave(memory_address, 0x800, + coreaux.bus) + self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800) + self.config["HAS_DRTIO"] = None + self.config["HAS_DRTIO_ROUTING"] = None + self.add_csr_group("drtio", drtio_csr_group) + self.add_csr_group("drtioaux", drtioaux_csr_group) + self.add_memory_group("drtioaux_mem", drtioaux_memory_group) + + self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6) + self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) + self.csr_devices.append("si5324_rst_n") + i2c = self.platform.request("i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.csr_devices.append("i2c") + self.config["I2C_BUS_COUNT"] = 1 + self.config["HAS_SI5324"] = None + self.config["SI5324_AS_SYNTHESIZER"] = None + + self.comb += [ + platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")), + platform.request("user_sma_clock_n").eq(ClockSignal("rtio")) + ] + + rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq + # Constrain TX & RX timing for the first transceiver channel + # (First channel acts as master for phase alignment for all channels' TX) + gtx0 = self.drtio_transceiver.gtxs[0] + platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period) + platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, + gtx0.txoutclk, gtx0.rxoutclk) + # Constrain RX timing for the each transceiver channel + # (Each channel performs single-lane phase alignment for RX) + for gtx in self.drtio_transceiver.gtxs[1:]: + platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, gtx0.txoutclk, gtx.rxoutclk) + + rtio_channels = [] + for i in range(8): + phy = ttl_simple.Output(platform.request("user_led", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + for sma in "user_sma_gpio_p", "user_sma_gpio_n": + phy = ttl_simple.InOut(platform.request(sma)) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + + self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) + self.csr_devices.append("rtio_moninj") + + self.submodules.rtio_core = rtio.Core(self.rtio_tsc, rtio_channels) + self.csr_devices.append("rtio_core") + + self.submodules.rtio = rtio.KernelInitiator(self.rtio_tsc) + self.submodules.rtio_dma = ClockDomainsRenamer("sys_kernel")( + rtio.DMA(self.get_native_sdram_if())) + self.register_kernel_cpu_csrdevice("rtio") + self.register_kernel_cpu_csrdevice("rtio_dma") + self.submodules.cri_con = rtio.CRIInterconnectShared( + [self.rtio.cri, self.rtio_dma.cri], + [self.rtio_core.cri] + drtio_cri, + enable_routing=True) + self.register_kernel_cpu_csrdevice("cri_con") + self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) + self.csr_devices.append("routing_table") + + +def main(): + parser = argparse.ArgumentParser( + description="ARTIQ device binary builder / KC705 DRTIO master") + builder_args(parser) + soc_kc705_args(parser) + parser.set_defaults(output_dir="artiq_kc705/master") + args = parser.parse_args() + + soc = Master(**soc_kc705_argdict(args)) + build_artiq_soc(soc, builder_argdict(args)) + + +if __name__ == "__main__": + main() diff --git a/artiq/gateware/targets/kc705_drtio_satellite.py b/artiq/gateware/targets/kc705_drtio_satellite.py new file mode 100755 index 000000000..4ff346fdf --- /dev/null +++ b/artiq/gateware/targets/kc705_drtio_satellite.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 + +import argparse + +from migen import * +from migen.build.generic_platform import * +from migen.build.xilinx.vivado import XilinxVivadoToolchain +from migen.build.xilinx.ise import XilinxISEToolchain + +from misoc.cores import spi as spi_csr +from misoc.cores import gpio +from misoc.integration.builder import * +from misoc.targets.kc705 import BaseSoC, soc_kc705_args, soc_kc705_argdict + +from artiq.gateware import rtio +from artiq.gateware.rtio.phy import ttl_simple +from artiq.gateware.drtio.transceiver import gtx_7series +from artiq.gateware.drtio.siphaser import SiPhaser7Series +from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer +from artiq.gateware.drtio import * +from artiq.build_soc import * + + +class Satellite(BaseSoC): + mem_map = { + "drtioaux": 0x50000000, + } + mem_map.update(BaseSoC.mem_map) + + def __init__(self, gateware_identifier_str=None, sma_as_sat=False, **kwargs): + BaseSoC.__init__(self, + cpu_type="or1k", + sdram_controller_type="minicon", + l2_size=128*1024, + integrated_sram_size=8192, + **kwargs) + add_identifier(self, gateware_identifier_str=gateware_identifier_str) + + if isinstance(self.platform.toolchain, XilinxVivadoToolchain): + self.platform.toolchain.bitstream_commands.extend([ + "set_property BITSTREAM.GENERAL.COMPRESS True [current_design]", + ]) + if isinstance(self.platform.toolchain, XilinxISEToolchain): + self.platform.toolchain.bitgen_opt += " -g compress" + + platform = self.platform + + self.comb += platform.request("sfp_tx_disable_n").eq(1) + tx_pads = [ + platform.request("sfp_tx"), platform.request("user_sma_mgt_tx") + ] + rx_pads = [ + platform.request("sfp_rx"), platform.request("user_sma_mgt_rx") + ] + if sma_as_sat: + tx_pads = tx_pads[::-1] + rx_pads = rx_pads[::-1] + + # 1000BASE_BX10 Ethernet compatible, 125MHz RTIO clock + self.submodules.drtio_transceiver = gtx_7series.GTX( + clock_pads=platform.request("si5324_clkout"), + tx_pads=tx_pads, + rx_pads=rx_pads, + sys_clk_freq=self.clk_freq) + self.csr_devices.append("drtio_transceiver") + + self.submodules.rtio_tsc = rtio.TSC("sync", glbl_fine_ts_width=3) + + drtioaux_csr_group = [] + drtioaux_memory_group = [] + drtiorep_csr_group = [] + self.drtio_cri = [] + for i in range(len(self.drtio_transceiver.channels)): + coreaux_name = "drtioaux" + str(i) + memory_name = "drtioaux" + str(i) + "_mem" + drtioaux_csr_group.append(coreaux_name) + drtioaux_memory_group.append(memory_name) + + cdr = ClockDomainsRenamer({"rtio_rx": "rtio_rx" + str(i)}) + + # Satellite + if i == 0: + self.submodules.rx_synchronizer = cdr(XilinxRXSynchronizer()) + core = cdr(DRTIOSatellite( + self.rtio_tsc, self.drtio_transceiver.channels[0], self.rx_synchronizer)) + self.submodules.drtiosat = core + self.csr_devices.append("drtiosat") + # Repeaters + else: + corerep_name = "drtiorep" + str(i-1) + drtiorep_csr_group.append(corerep_name) + core = cdr(DRTIORepeater( + self.rtio_tsc, self.drtio_transceiver.channels[i])) + setattr(self.submodules, corerep_name, core) + self.drtio_cri.append(core.cri) + self.csr_devices.append(corerep_name) + + coreaux = cdr(DRTIOAuxController(core.link_layer)) + setattr(self.submodules, coreaux_name, coreaux) + self.csr_devices.append(coreaux_name) + + memory_address = self.mem_map["drtioaux"] + 0x800*i + self.add_wb_slave(memory_address, 0x800, + coreaux.bus) + self.add_memory_region(memory_name, memory_address | self.shadow_base, 0x800) + self.config["HAS_DRTIO"] = None + self.config["HAS_DRTIO_ROUTING"] = None + self.add_csr_group("drtioaux", drtioaux_csr_group) + self.add_memory_group("drtioaux_mem", drtioaux_memory_group) + self.add_csr_group("drtiorep", drtiorep_csr_group) + + self.config["RTIO_FREQUENCY"] = str(self.drtio_transceiver.rtio_clk_freq/1e6) + # Si5324 Phaser + self.submodules.siphaser = SiPhaser7Series( + si5324_clkin=platform.request("si5324_clkin"), + rx_synchronizer=self.rx_synchronizer, + ultrascale=False, + rtio_clk_freq=self.drtio_transceiver.rtio_clk_freq) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) + self.csr_devices.append("siphaser") + self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) + self.csr_devices.append("si5324_rst_n") + i2c = self.platform.request("i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.csr_devices.append("i2c") + self.config["I2C_BUS_COUNT"] = 1 + self.config["HAS_SI5324"] = None + + self.comb += [ + platform.request("user_sma_clock_p").eq(ClockSignal("rtio_rx0")), + platform.request("user_sma_clock_n").eq(ClockSignal("rtio")) + ] + + rtio_clk_period = 1e9/self.drtio_transceiver.rtio_clk_freq + # Constrain TX & RX timing for the first transceiver channel + # (First channel acts as master for phase alignment for all channels' TX) + gtx0 = self.drtio_transceiver.gtxs[0] + platform.add_period_constraint(gtx0.txoutclk, rtio_clk_period) + platform.add_period_constraint(gtx0.rxoutclk, rtio_clk_period) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, + gtx0.txoutclk, gtx0.rxoutclk) + # Constrain RX timing for the each transceiver channel + # (Each channel performs single-lane phase alignment for RX) + for gtx in self.drtio_transceiver.gtxs[1:]: + platform.add_period_constraint(gtx.rxoutclk, rtio_clk_period) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, gtx.rxoutclk) + + rtio_channels = [] + for i in range(8): + phy = ttl_simple.Output(platform.request("user_led", i)) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + for sma in "user_sma_gpio_p", "user_sma_gpio_n": + phy = ttl_simple.InOut(platform.request(sma)) + self.submodules += phy + rtio_channels.append(rtio.Channel.from_phy(phy)) + + self.submodules.rtio_moninj = rtio.MonInj(rtio_channels) + self.csr_devices.append("rtio_moninj") + + self.submodules.local_io = SyncRTIO(self.rtio_tsc, rtio_channels) + self.comb += self.drtiosat.async_errors.eq(self.local_io.async_errors) + self.submodules.cri_con = rtio.CRIInterconnectShared( + [self.drtiosat.cri], + [self.local_io.cri] + self.drtio_cri, + mode="sync", enable_routing=True) + self.csr_devices.append("cri_con") + self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) + self.csr_devices.append("routing_table") + + +def main(): + parser = argparse.ArgumentParser( + description="ARTIQ device binary builder / KC705 DRTIO satellite") + builder_args(parser) + soc_kc705_args(parser) + parser.set_defaults(output_dir="artiq_kc705/satellite") + parser.add_argument("--sma", default=False, action="store_true", + help="use the SMA connectors (RX: J17, J18, TX: J19, J20) " + "as DRTIO satellite channel instead of the SFP") + args = parser.parse_args() + + argdict = dict() + argdict["sma_as_sat"] = args.sma + + soc = Satellite(**soc_kc705_argdict(args), **argdict) + build_artiq_soc(soc, builder_argdict(args)) + + +if __name__ == "__main__": + main()