sayma: automated FPGA SYSREF phase offset calibration

This commit is contained in:
Sebastien Bourdeauducq 2018-07-26 14:53:28 +08:00
parent 0a9d3638ee
commit d523d03f71
5 changed files with 158 additions and 76 deletions

View File

@ -34,7 +34,7 @@ fn read(addr: u16) -> u8 {
} }
} }
fn jesd_unreset() { pub fn jesd_unreset() {
unsafe { unsafe {
csr::ad9154_crg::ibuf_disable_write(0); csr::ad9154_crg::ibuf_disable_write(0);
csr::ad9154_crg::jreset_write(0); csr::ad9154_crg::jreset_write(0);
@ -760,17 +760,7 @@ fn init_dac(dacno: u8, sysref_phase: u16) -> Result<(), &'static str> {
Ok(()) Ok(())
} }
pub fn init(sysref_phase_fpga: u16, sysref_phase_dac: u16) { pub fn init(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);
for dacno in 0..csr::AD9154.len() { for dacno in 0..csr::AD9154.len() {
// We assume DCLK and SYSREF traces are matched on the PCB // We assume DCLK and SYSREF traces are matched on the PCB
// (they are on Sayma) so only one phase is needed. // (they are on Sayma) so only one phase is needed.

View File

@ -391,7 +391,7 @@ pub mod hmc7043 {
clock::spin_us(100); 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 analog_delay = (phase_offset % 17) as u8;
let digital_delay = (phase_offset / 17) as u8; let digital_delay = (phase_offset / 17) as u8;
spi_setup(); spi_setup();
@ -400,71 +400,12 @@ pub mod hmc7043 {
clock::spin_us(100); clock::spin_us(100);
} }
fn sysref_slip() { pub fn sysref_slip() {
spi_setup(); spi_setup();
write(0x0002, 0x02); write(0x0002, 0x02);
write(0x0002, 0x00); write(0x0002, 0x00);
clock::spin_us(100); 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> { pub fn init() -> Result<(), &'static str> {

View File

@ -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<u16, &'static str> {
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)
}

View File

@ -38,8 +38,11 @@ pub mod hmc830_7043;
mod ad9154_reg; mod ad9154_reg;
#[cfg(has_ad9154)] #[cfg(has_ad9154)]
pub mod ad9154; pub mod ad9154;
#[cfg(has_ad9154)]
pub mod jesd204sync;
#[cfg(has_allaki_atts)] #[cfg(has_allaki_atts)]
pub mod hmc542; pub mod hmc542;
#[cfg(has_grabber)] #[cfg(has_grabber)]
pub mod grabber; pub mod grabber;

View File

@ -56,8 +56,6 @@ mod moninj;
#[cfg(has_rtio_analyzer)] #[cfg(has_rtio_analyzer)]
mod analyzer; mod analyzer;
#[cfg(has_ad9154)]
const SYSREF_PHASE_FPGA: u16 = 41;
#[cfg(has_ad9154)] #[cfg(has_ad9154)]
const SYSREF_PHASE_DAC: u16 = 94; const SYSREF_PHASE_DAC: u16 = 94;
@ -114,7 +112,13 @@ fn startup() {
/* must be the first SPI init because of HMC830 SPI mode selection */ /* must be the first SPI init because of HMC830 SPI mode selection */
board_artiq::hmc830_7043::init().expect("cannot initialize HMC830/7043"); board_artiq::hmc830_7043::init().expect("cannot initialize HMC830/7043");
#[cfg(has_ad9154)] #[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)] #[cfg(has_allaki_atts)]
board_artiq::hmc542::program_all(8/*=4dB*/); board_artiq::hmc542::program_all(8/*=4dB*/);