From d523d03f71a7bddca79a21925f6b006bbbf4dbd8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 26 Jul 2018 14:53:28 +0800 Subject: [PATCH] sayma: automated FPGA SYSREF phase offset calibration --- artiq/firmware/libboard_artiq/ad9154.rs | 14 +- artiq/firmware/libboard_artiq/hmc830_7043.rs | 63 +------- artiq/firmware/libboard_artiq/jesd204sync.rs | 144 +++++++++++++++++++ artiq/firmware/libboard_artiq/lib.rs | 3 + artiq/firmware/runtime/main.rs | 10 +- 5 files changed, 158 insertions(+), 76 deletions(-) create mode 100644 artiq/firmware/libboard_artiq/jesd204sync.rs diff --git a/artiq/firmware/libboard_artiq/ad9154.rs b/artiq/firmware/libboard_artiq/ad9154.rs index 4b0e11be1..68ee06efa 100644 --- a/artiq/firmware/libboard_artiq/ad9154.rs +++ b/artiq/firmware/libboard_artiq/ad9154.rs @@ -34,7 +34,7 @@ fn read(addr: u16) -> u8 { } } -fn jesd_unreset() { +pub fn jesd_unreset() { unsafe { csr::ad9154_crg::ibuf_disable_write(0); csr::ad9154_crg::jreset_write(0); @@ -760,17 +760,7 @@ fn init_dac(dacno: u8, sysref_phase: u16) -> Result<(), &'static str> { Ok(()) } -pub fn init(sysref_phase_fpga: u16, sysref_phase_dac: u16) { - // Release the JESD clock domain reset late, as we need to - // set up clock chips before. - jesd_unreset(); - - // This needs to take place once before DAC SYSREF scan, as - // the HMC7043 input clock (which defines slip resolution) - // is 2x the DAC clock, so there are two possible phases from - // the divider states. This deterministically selects one. - hmc7043::sysref_rtio_align(sysref_phase_fpga, 1); - +pub fn init(sysref_phase_dac: u16) { for dacno in 0..csr::AD9154.len() { // We assume DCLK and SYSREF traces are matched on the PCB // (they are on Sayma) so only one phase is needed. diff --git a/artiq/firmware/libboard_artiq/hmc830_7043.rs b/artiq/firmware/libboard_artiq/hmc830_7043.rs index faa26630a..142ae11ea 100644 --- a/artiq/firmware/libboard_artiq/hmc830_7043.rs +++ b/artiq/firmware/libboard_artiq/hmc830_7043.rs @@ -391,7 +391,7 @@ pub mod hmc7043 { clock::spin_us(100); } - fn sysref_offset_fpga(phase_offset: u16) { + pub fn sysref_offset_fpga(phase_offset: u16) { let analog_delay = (phase_offset % 17) as u8; let digital_delay = (phase_offset / 17) as u8; spi_setup(); @@ -400,71 +400,12 @@ pub mod hmc7043 { clock::spin_us(100); } - fn sysref_slip() { + pub fn sysref_slip() { spi_setup(); write(0x0002, 0x02); write(0x0002, 0x00); clock::spin_us(100); } - - fn sysref_sample() -> bool { - unsafe { csr::sysref_sampler::sample_result_read() == 1 } - } - - pub fn sysref_rtio_align(phase_offset: u16, expected_align: u16) { - info!("aligning SYSREF with RTIO..."); - - let mut slips0 = 0; - let mut slips1 = 0; - // meet setup/hold (assuming FPGA timing margins are OK) - sysref_offset_fpga(phase_offset); - // if we are already in the 1 zone, get out of it - while sysref_sample() { - sysref_slip(); - slips0 += 1; - if slips0 > 1024 { - error!(" failed to reach 1->0 transition"); - break; - } - } - // get to the edge of the 0->1 transition (our final setpoint) - while !sysref_sample() { - sysref_slip(); - slips1 += 1; - if slips1 > 1024 { - error!(" failed to reach 0->1 transition"); - break; - } - } - info!(" ...done ({}/{} slips)", slips0, slips1); - if (slips0 + slips1) % expected_align != 0 { - error!(" unexpected slip alignment"); - } - - let mut margin_minus = None; - for d in 0..phase_offset { - sysref_offset_fpga(phase_offset - d); - if !sysref_sample() { - margin_minus = Some(d); - break; - } - } - // meet setup/hold - sysref_offset_fpga(phase_offset); - - if margin_minus.is_some() { - let margin_minus = margin_minus.unwrap(); - // one phase slip (period of the 1.2GHz input clock) - let period = 2*17; // approximate: 2 digital coarse delay steps - let margin_plus = if period > margin_minus { period - margin_minus } else { 0 }; - info!(" margins at FPGA: -{} +{}", margin_minus, margin_plus); - if margin_minus < 10 || margin_plus < 10 { - error!("SYSREF margin at FPGA is too small"); - } - } else { - error!("unable to determine SYSREF margin at FPGA"); - } - } } pub fn init() -> Result<(), &'static str> { diff --git a/artiq/firmware/libboard_artiq/jesd204sync.rs b/artiq/firmware/libboard_artiq/jesd204sync.rs new file mode 100644 index 000000000..3879c7b41 --- /dev/null +++ b/artiq/firmware/libboard_artiq/jesd204sync.rs @@ -0,0 +1,144 @@ +use board_misoc::csr; +use board_misoc::config; + +use hmc830_7043::hmc7043; + + +fn sysref_sample() -> bool { + unsafe { csr::sysref_sampler::sample_result_read() == 1 } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum SysrefSample { + Low, + High, + Unstable +} + +fn sysref_sample_stable(phase_offset: u16) -> SysrefSample { + hmc7043::sysref_offset_fpga(phase_offset); + let s1 = sysref_sample(); + hmc7043::sysref_offset_fpga(phase_offset-5); + let s2 = sysref_sample(); + if s1 == s2 { + if s1 { + return SysrefSample::High; + } else { + return SysrefSample::Low; + } + } else { + return SysrefSample::Unstable; + } +} + +fn sysref_cal_fpga() -> Result { + info!("calibrating SYSREF phase offset at FPGA..."); + + let initial_phase_offset = 136; + + let mut slips0 = 0; + let mut slips1 = 0; + // make sure we start in the 0 zone + while sysref_sample_stable(initial_phase_offset) != SysrefSample::Low { + hmc7043::sysref_slip(); + slips0 += 1; + if slips0 > 1024 { + return Err("failed to reach 1->0 transition (cal)"); + } + } + + // get near the edge of the 0->1 transition + while sysref_sample_stable(initial_phase_offset) != SysrefSample::High { + hmc7043::sysref_slip(); + slips1 += 1; + if slips1 > 1024 { + return Err("failed to reach 0->1 transition (cal)"); + } + } + + for d in 0..initial_phase_offset { + let phase_offset = initial_phase_offset - d; + hmc7043::sysref_offset_fpga(phase_offset); + if !sysref_sample() { + let result = phase_offset + 17; + info!(" ...done, phase offset: {}", result); + return Ok(result); + } + } + return Err("failed to reach 1->0 transition with fine delay"); +} + +fn sysref_rtio_align(phase_offset: u16, expected_align: u16) -> Result<(), &'static str> { + // This needs to take place once before DAC SYSREF scan, as + // the HMC7043 input clock (which defines slip resolution) + // is 2x the DAC clock, so there are two possible phases from + // the divider states. This deterministically selects one. + + info!("aligning SYSREF with RTIO..."); + + let mut slips0 = 0; + let mut slips1 = 0; + // meet setup/hold (assuming FPGA timing margins are OK) + hmc7043::sysref_offset_fpga(phase_offset); + // if we are already in the 1 zone, get out of it + while sysref_sample() { + hmc7043::sysref_slip(); + slips0 += 1; + if slips0 > 1024 { + return Err("failed to reach 1->0 transition"); + } + } + // get to the edge of the 0->1 transition (our final setpoint) + while !sysref_sample() { + hmc7043::sysref_slip(); + slips1 += 1; + if slips1 > 1024 { + return Err("failed to reach 0->1 transition"); + } + } + info!(" ...done ({}/{} slips)", slips0, slips1); + if (slips0 + slips1) % expected_align != 0 { + return Err("unexpected slip alignment"); + } + + let mut margin_minus = None; + for d in 0..phase_offset { + hmc7043::sysref_offset_fpga(phase_offset - d); + if !sysref_sample() { + margin_minus = Some(d); + break; + } + } + // meet setup/hold + hmc7043::sysref_offset_fpga(phase_offset); + + if margin_minus.is_some() { + let margin_minus = margin_minus.unwrap(); + // one phase slip (period of the 1.2GHz input clock) + let period = 2*17; // approximate: 2 digital coarse delay steps + let margin_plus = if period > margin_minus { period - margin_minus } else { 0 }; + info!(" margins at FPGA: -{} +{}", margin_minus, margin_plus); + if margin_minus < 10 || margin_plus < 10 { + return Err("SYSREF margin at FPGA is too small, board needs recalibration"); + } + } else { + return Err("unable to determine SYSREF margin at FPGA"); + } + + Ok(()) +} + +pub fn sysref_auto_rtio_align(expected_align: u16) -> Result<(), &'static str> { + let entry = config::read_str("sysref_phase_fpga", |r| r.map(|s| s.parse())); + let phase_offset = match entry { + Ok(Ok(phase)) => phase, + _ => { + let phase = sysref_cal_fpga()?; + if let Err(e) = config::write_int("sysref_phase_fpga", phase as u32) { + error!("failed to update FPGA SYSREF phase in config: {}", e); + } + phase + } + }; + sysref_rtio_align(phase_offset, expected_align) +} diff --git a/artiq/firmware/libboard_artiq/lib.rs b/artiq/firmware/libboard_artiq/lib.rs index 6c26e8d0a..443737441 100644 --- a/artiq/firmware/libboard_artiq/lib.rs +++ b/artiq/firmware/libboard_artiq/lib.rs @@ -38,8 +38,11 @@ pub mod hmc830_7043; mod ad9154_reg; #[cfg(has_ad9154)] pub mod ad9154; +#[cfg(has_ad9154)] +pub mod jesd204sync; #[cfg(has_allaki_atts)] pub mod hmc542; + #[cfg(has_grabber)] pub mod grabber; diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index b1615753c..edb9e4393 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -56,8 +56,6 @@ mod moninj; #[cfg(has_rtio_analyzer)] mod analyzer; -#[cfg(has_ad9154)] -const SYSREF_PHASE_FPGA: u16 = 41; #[cfg(has_ad9154)] const SYSREF_PHASE_DAC: u16 = 94; @@ -114,7 +112,13 @@ fn startup() { /* must be the first SPI init because of HMC830 SPI mode selection */ board_artiq::hmc830_7043::init().expect("cannot initialize HMC830/7043"); #[cfg(has_ad9154)] - board_artiq::ad9154::init(SYSREF_PHASE_FPGA, SYSREF_PHASE_DAC); + { + board_artiq::ad9154::jesd_unreset(); + if let Err(e) = board_artiq::jesd204sync::sysref_auto_rtio_align(1) { + error!("failed to align SYSREF at FPGA: {}", e); + } + board_artiq::ad9154::init(SYSREF_PHASE_DAC); + } #[cfg(has_allaki_atts)] board_artiq::hmc542::program_all(8/*=4dB*/);