From 32c95ac034df96ebe836f8b8286f583a5e36a5ae Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Thu, 26 Jul 2018 16:22:08 +0800 Subject: [PATCH] sayma: automated DAC SYSREF phase calibration --- artiq/firmware/libboard_artiq/ad9154.rs | 66 ++-------- artiq/firmware/libboard_artiq/jesd204sync.rs | 129 ++++++++++++++++++- artiq/firmware/runtime/main.rs | 8 +- 3 files changed, 138 insertions(+), 65 deletions(-) diff --git a/artiq/firmware/libboard_artiq/ad9154.rs b/artiq/firmware/libboard_artiq/ad9154.rs index 2c3bb9339..978f2a199 100644 --- a/artiq/firmware/libboard_artiq/ad9154.rs +++ b/artiq/firmware/libboard_artiq/ad9154.rs @@ -1,6 +1,5 @@ use board_misoc::{csr, clock}; use ad9154_reg; -use hmc830_7043::hmc7043; fn spi_setup(dacno: u8) { unsafe { @@ -688,7 +687,7 @@ fn dac_cfg_retry(dacno: u8) -> Result<(), &'static str> { } } -fn dac_get_sync_error(dacno: u8) -> u16 { +pub fn dac_get_sync_error(dacno: u8) -> u16 { spi_setup(dacno); let sync_error = ((read(ad9154_reg::SYNC_CURRERR_L) as u16) | ((read(ad9154_reg::SYNC_CURRERR_H) as u16) << 8)) @@ -696,73 +695,24 @@ fn dac_get_sync_error(dacno: u8) -> u16 { sync_error } -fn dac_sysref_scan(dacno: u8, center_phase: u16) { - let mut margin_minus = None; - let mut margin_plus = None; - - info!("AD9154-{} SYSREF scan...", dacno); - - hmc7043::sysref_offset_dac(dacno, center_phase); - clock::spin_us(10000); - let mut sync_error_last = dac_get_sync_error(dacno); - for d in 0..128 { - hmc7043::sysref_offset_dac(dacno, center_phase - d); - clock::spin_us(10000); - let sync_error = dac_get_sync_error(dacno); - if sync_error != sync_error_last { - info!(" sync error-: {} -> {}", sync_error_last, sync_error); - margin_minus = Some(d); - break; - } - } - - hmc7043::sysref_offset_dac(dacno, center_phase); - clock::spin_us(10000); - sync_error_last = dac_get_sync_error(dacno); - for d in 0..128 { - hmc7043::sysref_offset_dac(dacno, center_phase + d); - clock::spin_us(10000); - let sync_error = dac_get_sync_error(dacno); - if sync_error != sync_error_last { - info!(" sync error+: {} -> {}", sync_error_last, sync_error); - margin_plus = Some(d); - break; - } - } - - if margin_minus.is_some() && margin_plus.is_some() { - let margin_minus = margin_minus.unwrap(); - let margin_plus = margin_plus.unwrap(); - info!(" margins: -{} +{}", margin_minus, margin_plus); - if margin_minus < 10 || margin_plus < 10 { - error!("SYSREF margins are too small"); - } - } else { - error!("Unable to determine SYSREF margins"); - } -} - -fn init_dac(dacno: u8, sysref_phase: u16) -> Result<(), &'static str> { +fn init_dac(dacno: u8) -> Result<(), &'static str> { let dacno = dacno as u8; - // Reset the DAC, detect and configure it + dac_reset(dacno); dac_detect(dacno)?; dac_cfg_retry(dacno)?; - // Run the PRBS, STPL and SYSREF scan tests + dac_prbs(dacno)?; dac_stpl(dacno, 4, 2)?; - dac_sysref_scan(dacno, sysref_phase); - // Set SYSREF phase and reconfigure the DAC - hmc7043::sysref_offset_dac(dacno, sysref_phase); + dac_cfg_retry(dacno)?; + Ok(()) } -pub fn init(sysref_phase_dac: u16) { +pub fn init() { 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. - match init_dac(dacno as u8, sysref_phase_dac) { + match init_dac(dacno as u8) { Ok(_) => (), Err(e) => error!("failed to initialize AD9154-{}: {}", dacno, e) } diff --git a/artiq/firmware/libboard_artiq/jesd204sync.rs b/artiq/firmware/libboard_artiq/jesd204sync.rs index 3879c7b41..9eeaca300 100644 --- a/artiq/firmware/libboard_artiq/jesd204sync.rs +++ b/artiq/firmware/libboard_artiq/jesd204sync.rs @@ -1,8 +1,7 @@ -use board_misoc::csr; -use board_misoc::config; +use board_misoc::{csr, clock, config}; use hmc830_7043::hmc7043; - +use ad9154; fn sysref_sample() -> bool { unsafe { csr::sysref_sampler::sample_result_read() == 1 } @@ -142,3 +141,127 @@ pub fn sysref_auto_rtio_align(expected_align: u16) -> Result<(), &'static str> { }; sysref_rtio_align(phase_offset, expected_align) } + +fn sysref_cal_dac(dacno: u8) -> Result { + info!("calibrating SYSREF phase at DAC-{}...", dacno); + + let mut d = 0; + let dmin; + let dmax; + + hmc7043::sysref_offset_dac(dacno, d); + clock::spin_us(10000); + let sync_error_last = ad9154::dac_get_sync_error(dacno); + + loop { + hmc7043::sysref_offset_dac(dacno, d); + clock::spin_us(10000); + let sync_error = ad9154::dac_get_sync_error(dacno); + if sync_error != sync_error_last { + dmin = d; + break; + } + + d += 1; + if d > 128 { + return Err("no sync errors found when scanning delay"); + } + } + + d += 5; // get away from jitter + hmc7043::sysref_offset_dac(dacno, d); + clock::spin_us(10000); + let sync_error_last = ad9154::dac_get_sync_error(dacno); + + loop { + hmc7043::sysref_offset_dac(dacno, d); + clock::spin_us(10000); + let sync_error = ad9154::dac_get_sync_error(dacno); + if sync_error != sync_error_last { + dmax = d; + break; + } + + d += 1; + if d > 128 { + return Err("no sync errors found when scanning delay"); + } + } + + let phase = (dmin+dmax)/2; + info!(" ...done, min={}, max={}, result={}", dmin, dmax, phase); + Ok(phase) +} + +fn sysref_dac_align(dacno: u8, phase: u16) -> Result<(), &'static str> { + let mut margin_minus = None; + let mut margin_plus = None; + + info!("verifying SYSREF margins at DAC-{}...", dacno); + + hmc7043::sysref_offset_dac(dacno, phase); + clock::spin_us(10000); + let sync_error_last = ad9154::dac_get_sync_error(dacno); + for d in 0..128 { + hmc7043::sysref_offset_dac(dacno, phase - d); + clock::spin_us(10000); + let sync_error = ad9154::dac_get_sync_error(dacno); + if sync_error != sync_error_last { + info!(" sync error-: {} -> {}", sync_error_last, sync_error); + margin_minus = Some(d); + break; + } + } + + hmc7043::sysref_offset_dac(dacno, phase); + clock::spin_us(10000); + let sync_error_last = ad9154::dac_get_sync_error(dacno); + for d in 0..128 { + hmc7043::sysref_offset_dac(dacno, phase + d); + clock::spin_us(10000); + let sync_error = ad9154::dac_get_sync_error(dacno); + if sync_error != sync_error_last { + info!(" sync error+: {} -> {}", sync_error_last, sync_error); + margin_plus = Some(d); + break; + } + } + + if margin_minus.is_some() && margin_plus.is_some() { + let margin_minus = margin_minus.unwrap(); + let margin_plus = margin_plus.unwrap(); + info!(" margins: -{} +{}", margin_minus, margin_plus); + if margin_minus < 10 || margin_plus < 10 { + return Err("SYSREF margins at DAC are too small, board needs recalibration"); + } + } else { + return Err("Unable to determine SYSREF margins at DAC"); + } + + // Leave SYSREF at the correct setting + hmc7043::sysref_offset_dac(dacno, phase); + + Ok(()) +} + +pub fn sysref_auto_dac_align() -> Result<(), &'static str> { + // We assume that DAC SYSREF traces are length-matched so only one phase + // value is needed, and we use DAC-0 as calibration reference. + + let entry = config::read_str("sysref_phase_dac", |r| r.map(|s| s.parse())); + let phase = match entry { + Ok(Ok(phase)) => phase, + _ => { + let phase = sysref_cal_dac(0)?; + if let Err(e) = config::write_int("sysref_phase_dac", phase as u32) { + error!("failed to update DAC SYSREF phase in config: {}", e); + } + phase + } + }; + + for dacno in 0..csr::AD9154.len() { + sysref_dac_align(dacno as u8, phase)?; + } + Ok(()) +} diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index edb9e4393..e559db900 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -56,9 +56,6 @@ mod moninj; #[cfg(has_rtio_analyzer)] mod analyzer; -#[cfg(has_ad9154)] -const SYSREF_PHASE_DAC: u16 = 94; - fn startup() { irq::set_mask(0); irq::set_ie(true); @@ -114,10 +111,13 @@ fn startup() { #[cfg(has_ad9154)] { board_artiq::ad9154::jesd_unreset(); + board_artiq::ad9154::init(); 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); + if let Err(e) = board_artiq::jesd204sync::sysref_auto_dac_align() { + error!("failed to align SYSREF at DAC: {}", e); + } } #[cfg(has_allaki_atts)] board_artiq::hmc542::program_all(8/*=4dB*/);