diff --git a/artiq/firmware/runtime/rtio_clocking.rs b/artiq/firmware/runtime/rtio_clocking.rs index 99a47fe3f..9a36326f4 100644 --- a/artiq/firmware/runtime/rtio_clocking.rs +++ b/artiq/firmware/runtime/rtio_clocking.rs @@ -2,7 +2,7 @@ use board_misoc::config; #[cfg(si5324_as_synthesizer)] use board_artiq::si5324; #[cfg(has_drtio)] -use board_misoc::csr; +use board_misoc::{csr, clock}; #[derive(Debug)] pub enum RtioClock { @@ -161,6 +161,10 @@ pub fn init() { unsafe { csr::drtio_transceiver::stable_clkin_write(1); } + clock::spin_us(1500); // wait for CPLL/QPLL lock + unsafe { + csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); + } #[cfg(has_rtio_crg)] { diff --git a/artiq/firmware/satman/jdcg.rs b/artiq/firmware/satman/jdcg.rs index 4f8736fe7..a497ce505 100644 --- a/artiq/firmware/satman/jdcg.rs +++ b/artiq/firmware/satman/jdcg.rs @@ -160,7 +160,6 @@ pub mod jesd204sync { } } - fn measure_ddmdt_phase_raw() -> Result { Ok(jdac::basic_request(0, jdac_common::DDMTD_SYSREF_RAW, 0)? as i32) } @@ -567,4 +566,12 @@ pub mod jesd204sync { error!("failed to align SYSREF at DAC: {}", e); } } + + pub fn resync_dacs() -> Result<(), &'static str> { + info!("resychronizing DACs"); + for dacno in 0..csr::JDCG.len() { + ad9154_sync(dacno as u8)?; + } + Ok(()) + } } diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 12d80c2d0..8a07db183 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -465,6 +465,10 @@ pub extern fn main() -> i32 { csr::drtio_transceiver::stable_clkin_write(1); } clock::spin_us(1500); // wait for CPLL/QPLL lock + #[cfg(not(has_jdcg))] + unsafe { + csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); + } #[cfg(has_wrpll)] wrpll::diagnostics(); init_rtio_crg(); @@ -493,6 +497,11 @@ pub extern fn main() -> i32 { let mut hardware_tick_ts = 0; loop { + #[cfg(has_jdcg)] + unsafe { + // Hide from uplink until RTM is ready + csr::drtio_transceiver::txenable_write(0xfffffffeu32 as _); + } while !drtiosat_link_rx_up() { drtiosat_process_errors(); for mut rep in repeaters.iter_mut() { @@ -510,33 +519,12 @@ pub extern fn main() -> i32 { #[cfg(has_wrpll)] wrpll::select_recovered_clock(true); - #[cfg(has_jdcg)] - { - /* - * One side of the JESD204 elastic buffer is clocked by the Si5324, the other - * by the RTM. - * The elastic buffer can operate only when those two clocks are derived from - * the same oscillator. - * This is the case when either of those conditions is true: - * (1) The DRTIO master and the RTM are clocked directly from a common external - * source, *and* the Si5324 has locked to the recovered clock. - * This clocking scheme provides less noise and phase drift at the DACs. - * (2) The RTM clock is connected to the Si5324 output. - * To handle those cases, we simply keep the JESD204 core in reset unless the - * Si5324 is locked to the recovered clock. - */ - jdcg::jesd::reset(false); - if repeaters[0].is_up() { - let _ = jdcg::jdac::init(); - } - } - drtioaux::reset(0); drtiosat_reset(false); drtiosat_reset_phy(false); #[cfg(has_jdcg)] - let mut rep0_was_up = repeaters[0].is_up(); + let mut was_up = false; while drtiosat_link_rx_up() { drtiosat_process_errors(); process_aux_packets(&mut repeaters, &mut routing_table, &mut rank); @@ -548,8 +536,14 @@ pub extern fn main() -> i32 { info!("TSC loaded from uplink"); #[cfg(has_jdcg)] { - if rep0_was_up { - jdcg::jesd204sync::sysref_auto_align(); + // We assume that the RTM on repeater0 is up. + // Uplink should not send a TSC load command unless the link is + // up, and we are hiding when the RTM is down. + if let Err(e) = jdcg::jesd204sync::sysref_rtio_align() { + error!("failed to align SYSREF with TSC ({})", e); + } + if let Err(e) = jdcg::jesd204sync::resync_dacs() { + error!("DAC resync failed after SYSREF/TSC realignment ({})", e); } } for rep in repeaters.iter() { @@ -563,12 +557,29 @@ pub extern fn main() -> i32 { } #[cfg(has_jdcg)] { - let rep0_is_up = repeaters[0].is_up(); - if rep0_is_up && !rep0_was_up { + let is_up = repeaters[0].is_up(); + if is_up && !was_up { + /* + * One side of the JESD204 elastic buffer is clocked by the jitter filter + * (Si5324 or WRPLL), the other by the RTM. + * The elastic buffer can operate only when those two clocks are derived from + * the same oscillator. + * This is the case when either of those conditions is true: + * (1) The DRTIO master and the RTM are clocked directly from a common external + * source, *and* the jitter filter has locked to the recovered clock. + * This clocking scheme may provide less noise and phase drift at the DACs. + * (2) The RTM clock is connected to the jitter filter output. + * To handle those cases, we simply keep the JESD204 core in reset unless the + * jitter filter is locked to the recovered clock. + */ + jdcg::jesd::reset(false); let _ = jdcg::jdac::init(); jdcg::jesd204sync::sysref_auto_align(); + unsafe { + csr::drtio_transceiver::txenable_write(0xffffffffu32 as _); // unhide + } } - rep0_was_up = rep0_is_up; + was_up = is_up; } } diff --git a/artiq/gateware/drtio/core.py b/artiq/gateware/drtio/core.py index 55176b1ce..6311456e4 100644 --- a/artiq/gateware/drtio/core.py +++ b/artiq/gateware/drtio/core.py @@ -30,6 +30,7 @@ class ChannelInterface: class TransceiverInterface(AutoCSR): def __init__(self, channel_interfaces): self.stable_clkin = CSRStorage() + self.txenable = CSRStorage(len(channel_interfaces)) self.clock_domains.cd_rtio = ClockDomain() for i in range(len(channel_interfaces)): name = "rtio_rx" + str(i) diff --git a/artiq/gateware/drtio/transceiver/gth_ultrascale.py b/artiq/gateware/drtio/transceiver/gth_ultrascale.py index ea7f908f5..ddc88037a 100644 --- a/artiq/gateware/drtio/transceiver/gth_ultrascale.py +++ b/artiq/gateware/drtio/transceiver/gth_ultrascale.py @@ -29,6 +29,7 @@ class GTHSingle(Module): # # # + self.txenable = Signal() nwords = dw//10 self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")( Encoder(nwords, True)) @@ -467,7 +468,6 @@ class GTHSingle(Module): i_GTREFCLK0=refclk, # TX clock - o_TXOUTCLK=self.txoutclk, i_TXSYSCLKSEL=0b00, i_TXPLLCLKSEL=0b00, @@ -487,7 +487,7 @@ class GTHSingle(Module): 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)]), @@ -675,6 +675,8 @@ class GTH(Module, TransceiverInterface): 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, diff --git a/artiq/gateware/drtio/transceiver/gtp_7series.py b/artiq/gateware/drtio/transceiver/gtp_7series.py index 98360ca2d..5da42b22b 100644 --- a/artiq/gateware/drtio/transceiver/gtp_7series.py +++ b/artiq/gateware/drtio/transceiver/gtp_7series.py @@ -19,6 +19,7 @@ class GTPSingle(Module): # # # self.stable_clkin = Signal() + self.txenable = Signal() self.submodules.encoder = encoder = ClockDomainsRenamer("rtio_tx")( Encoder(2, True)) self.submodules.decoders = decoders = [ClockDomainsRenamer("rtio_rx")( @@ -611,7 +612,7 @@ class GTPSingle(Module): i_TXDEEMPH =0, i_TXDIFFCTRL =0b1000, i_TXDIFFPD =0, - i_TXINHIBIT =0, + i_TXINHIBIT =~self.txenable, i_TXMAINCURSOR =0b0000000, i_TXPISOPD =0, # Transmit Ports - TX Fabric Clock Output Control Ports @@ -747,8 +748,11 @@ class GTP(Module, TransceiverInterface): self.submodules.tx_phase_alignment = GTPTXPhaseAlignement(self.gtps) TransceiverInterface.__init__(self, channel_interfaces) - for gtp in self.gtps: - self.comb += gtp.stable_clkin.eq(self.stable_clkin.storage) + for n, gtp in enumerate(self.gtps): + self.comb += [ + gtp.stable_clkin.eq(self.stable_clkin.storage), + gtp.txenable.eq(self.txenable.storage[n]) + ] self.comb += [ self.cd_rtio.clk.eq(self.gtps[master].cd_rtio_tx.clk),