forked from M-Labs/artiq
sayma: automated FPGA SYSREF phase offset calibration
This commit is contained in:
parent
0a9d3638ee
commit
d523d03f71
|
@ -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.
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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*/);
|
||||
|
||||
|
|
Loading…
Reference in New Issue