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 {
|
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.
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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;
|
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;
|
||||||
|
|
||||||
|
|
|
@ -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*/);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue