mirror of https://github.com/m-labs/artiq.git
sayma2: JESD204 synchronization
This commit is contained in:
parent
62b49882b9
commit
05e8f24c24
|
@ -139,14 +139,12 @@ mod hmc830 {
|
||||||
pub mod hmc7043 {
|
pub mod hmc7043 {
|
||||||
use board_misoc::{csr, clock};
|
use board_misoc::{csr, clock};
|
||||||
|
|
||||||
pub const ANALOG_DELAY_RANGE: u8 = 24;
|
|
||||||
|
|
||||||
// Warning: dividers are not synchronized with HMC830 clock input!
|
// Warning: dividers are not synchronized with HMC830 clock input!
|
||||||
// Set DAC_CLK_DIV to 1 or 0 for deterministic phase.
|
// Set DAC_CLK_DIV to 1 or 0 for deterministic phase.
|
||||||
// (0 bypasses the divider and reduces noise)
|
// (0 bypasses the divider and reduces noise)
|
||||||
pub const DAC_CLK_DIV: u16 = 0;
|
const DAC_CLK_DIV: u16 = 0;
|
||||||
pub const FPGA_CLK_DIV: u16 = 16;
|
const FPGA_CLK_DIV: u16 = 16; // Keep in sync with jdcg.rs
|
||||||
pub const SYSREF_DIV: u16 = 256;
|
const SYSREF_DIV: u16 = 256; // Keep in sync with jdcg.rs
|
||||||
const HMC_SYSREF_DIV: u16 = SYSREF_DIV*8; // must be <= 4MHz
|
const HMC_SYSREF_DIV: u16 = SYSREF_DIV*8; // must be <= 4MHz
|
||||||
|
|
||||||
// enabled, divider, output config, is sysref
|
// enabled, divider, output config, is sysref
|
||||||
|
|
|
@ -1,451 +0,0 @@
|
||||||
use board_misoc::{csr, clock, config};
|
|
||||||
|
|
||||||
use hmc830_7043::hmc7043;
|
|
||||||
use ad9154;
|
|
||||||
|
|
||||||
fn average_2phases(a: i32, b:i32, modulo: i32) -> i32 {
|
|
||||||
let diff = ((a - b + modulo/2 + modulo) % modulo) - modulo/2;
|
|
||||||
return (modulo + b + diff/2) % modulo;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn average_phases(phases: &[i32], modulo: i32) -> i32 {
|
|
||||||
if phases.len() == 1 {
|
|
||||||
panic!("input array length must be a power of 2");
|
|
||||||
} else if phases.len() == 2 {
|
|
||||||
average_2phases(phases[0], phases[1], modulo)
|
|
||||||
} else {
|
|
||||||
let cut = phases.len()/2;
|
|
||||||
average_2phases(
|
|
||||||
average_phases(&phases[..cut], modulo),
|
|
||||||
average_phases(&phases[cut..], modulo),
|
|
||||||
modulo)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const RAW_DDMTD_N_SHIFT: i32 = 6;
|
|
||||||
const RAW_DDMTD_N: i32 = 1 << RAW_DDMTD_N_SHIFT;
|
|
||||||
const DDMTD_DITHER_BITS: i32 = 1;
|
|
||||||
const DDMTD_N_SHIFT: i32 = RAW_DDMTD_N_SHIFT + DDMTD_DITHER_BITS;
|
|
||||||
const DDMTD_N: i32 = 1 << DDMTD_N_SHIFT;
|
|
||||||
|
|
||||||
fn init_ddmtd() -> Result<(), &'static str> {
|
|
||||||
unsafe {
|
|
||||||
csr::sysref_ddmtd::reset_write(1);
|
|
||||||
clock::spin_us(1);
|
|
||||||
csr::sysref_ddmtd::reset_write(0);
|
|
||||||
clock::spin_us(100);
|
|
||||||
if csr::sysref_ddmtd::locked_read() != 0 {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err("DDMTD helper PLL failed to lock")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure_ddmdt_phase_raw() -> i32 {
|
|
||||||
unsafe { csr::sysref_ddmtd::dt_read() as i32 }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure_ddmdt_phase() -> i32 {
|
|
||||||
const AVG_PRECISION_SHIFT: i32 = 6;
|
|
||||||
const AVG_PRECISION: i32 = 1 << AVG_PRECISION_SHIFT;
|
|
||||||
const AVG_MOD: i32 = 1 << (RAW_DDMTD_N_SHIFT + AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
|
|
||||||
|
|
||||||
let mut measurements = [0; AVG_PRECISION as usize];
|
|
||||||
for i in 0..AVG_PRECISION {
|
|
||||||
measurements[i as usize] = measure_ddmdt_phase_raw() << (AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
|
|
||||||
clock::spin_us(10);
|
|
||||||
}
|
|
||||||
average_phases(&measurements, AVG_MOD) >> AVG_PRECISION_SHIFT
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_ddmtd_stability(raw: bool, tolerance: i32) -> Result<(), &'static str> {
|
|
||||||
info!("testing DDMTD stability (raw={}, tolerance={})...", raw, tolerance);
|
|
||||||
|
|
||||||
let modulo = if raw { RAW_DDMTD_N } else { DDMTD_N };
|
|
||||||
let measurement = if raw { measure_ddmdt_phase_raw } else { measure_ddmdt_phase };
|
|
||||||
let ntests = if raw { 15000 } else { 150 };
|
|
||||||
|
|
||||||
let mut max_pkpk = 0;
|
|
||||||
for _ in 0..32 {
|
|
||||||
// If we are near the edges, wraparound can throw off the simple min/max computation.
|
|
||||||
// In this case, add an offset to get near the center.
|
|
||||||
let quadrant = measure_ddmdt_phase();
|
|
||||||
let center_offset =
|
|
||||||
if quadrant < DDMTD_N/4 || quadrant > 3*DDMTD_N/4 {
|
|
||||||
modulo/2
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut min = modulo;
|
|
||||||
let mut max = 0;
|
|
||||||
for _ in 0..ntests {
|
|
||||||
let m = (measurement() + center_offset) % modulo;
|
|
||||||
if m < min {
|
|
||||||
min = m;
|
|
||||||
}
|
|
||||||
if m > max {
|
|
||||||
max = m;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let pkpk = max - min;
|
|
||||||
if pkpk > max_pkpk {
|
|
||||||
max_pkpk = pkpk;
|
|
||||||
}
|
|
||||||
if pkpk > tolerance {
|
|
||||||
error!(" ...excessive peak-peak jitter: {} (min={} max={} center_offset={})", pkpk,
|
|
||||||
min, max, center_offset);
|
|
||||||
return Err("excessive DDMTD peak-peak jitter");
|
|
||||||
}
|
|
||||||
hmc7043::sysref_slip();
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...passed, peak-peak jitter: {}", max_pkpk);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test_slip_ddmtd() -> Result<(), &'static str> {
|
|
||||||
// expected_step = (RTIO clock frequency)*(DDMTD N)/(HMC7043 CLKIN frequency)
|
|
||||||
let expected_step = 8;
|
|
||||||
let tolerance = 1;
|
|
||||||
|
|
||||||
info!("testing HMC7043 SYSREF slip against DDMTD...");
|
|
||||||
let mut old_phase = measure_ddmdt_phase();
|
|
||||||
for _ in 0..1024 {
|
|
||||||
hmc7043::sysref_slip();
|
|
||||||
let phase = measure_ddmdt_phase();
|
|
||||||
let step = (DDMTD_N + old_phase - phase) % DDMTD_N;
|
|
||||||
if (step - expected_step).abs() > tolerance {
|
|
||||||
error!(" ...got unexpected step: {} ({} -> {})", step, old_phase, phase);
|
|
||||||
return Err("HMC7043 SYSREF slip produced unexpected DDMTD step");
|
|
||||||
}
|
|
||||||
old_phase = phase;
|
|
||||||
}
|
|
||||||
info!(" ...passed");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_sh_error() -> bool {
|
|
||||||
unsafe {
|
|
||||||
csr::sysref_sampler::sh_error_reset_write(1);
|
|
||||||
clock::spin_us(1);
|
|
||||||
csr::sysref_sampler::sh_error_reset_write(0);
|
|
||||||
clock::spin_us(10);
|
|
||||||
csr::sysref_sampler::sh_error_read() != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SYSREF_SH_PRECISION_SHIFT: i32 = 5;
|
|
||||||
const SYSREF_SH_PRECISION: i32 = 1 << SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
const SYSREF_SH_MOD: i32 = 1 << (DDMTD_N_SHIFT + SYSREF_SH_PRECISION_SHIFT);
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct SysrefShLimits {
|
|
||||||
rising_phases: [i32; SYSREF_SH_PRECISION as usize],
|
|
||||||
falling_phases: [i32; SYSREF_SH_PRECISION as usize],
|
|
||||||
}
|
|
||||||
|
|
||||||
fn measure_sysref_sh_limits() -> Result<SysrefShLimits, &'static str> {
|
|
||||||
let mut ret = SysrefShLimits::default();
|
|
||||||
let mut nslips = 0;
|
|
||||||
let mut rising_n = 0;
|
|
||||||
let mut falling_n = 0;
|
|
||||||
|
|
||||||
let mut previous = sysref_sh_error();
|
|
||||||
while rising_n < SYSREF_SH_PRECISION || falling_n < SYSREF_SH_PRECISION {
|
|
||||||
hmc7043::sysref_slip();
|
|
||||||
nslips += 1;
|
|
||||||
if nslips > 1024 {
|
|
||||||
return Err("too many slips and not enough SYSREF S/H error transitions");
|
|
||||||
}
|
|
||||||
|
|
||||||
let current = sysref_sh_error();
|
|
||||||
let phase = measure_ddmdt_phase();
|
|
||||||
if current && !previous && rising_n < SYSREF_SH_PRECISION {
|
|
||||||
ret.rising_phases[rising_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
rising_n += 1;
|
|
||||||
}
|
|
||||||
if !current && previous && falling_n < SYSREF_SH_PRECISION {
|
|
||||||
ret.falling_phases[falling_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
falling_n += 1;
|
|
||||||
}
|
|
||||||
previous = current;
|
|
||||||
}
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn max_phase_deviation(average: i32, phases: &[i32]) -> i32 {
|
|
||||||
let mut ret = 0;
|
|
||||||
for phase in phases.iter() {
|
|
||||||
let deviation = (phase - average + DDMTD_N) % DDMTD_N;
|
|
||||||
if deviation > ret {
|
|
||||||
ret = deviation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reach_sysref_ddmtd_target(target: i32, tolerance: i32) -> Result<i32, &'static str> {
|
|
||||||
for _ in 0..1024 {
|
|
||||||
let delta = (measure_ddmdt_phase() - target + DDMTD_N) % DDMTD_N;
|
|
||||||
if delta <= tolerance {
|
|
||||||
return Ok(delta)
|
|
||||||
}
|
|
||||||
hmc7043::sysref_slip();
|
|
||||||
}
|
|
||||||
Err("failed to reach SYSREF DDMTD phase target")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calibrate_sysref_target(rising_average: i32, falling_average: i32) -> Result<i32, &'static str> {
|
|
||||||
info!("calibrating SYSREF DDMTD target phase...");
|
|
||||||
let coarse_target =
|
|
||||||
if rising_average < falling_average {
|
|
||||||
(rising_average + falling_average)/2
|
|
||||||
} else {
|
|
||||||
((falling_average - (DDMTD_N - rising_average))/2 + DDMTD_N) % DDMTD_N
|
|
||||||
};
|
|
||||||
info!(" SYSREF calibration coarse target: {}", coarse_target);
|
|
||||||
reach_sysref_ddmtd_target(coarse_target, 8)?;
|
|
||||||
let target = measure_ddmdt_phase();
|
|
||||||
info!(" ...done, target={}", target);
|
|
||||||
Ok(target)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_get_tsc_phase_raw() -> Result<u8, &'static str> {
|
|
||||||
if sysref_sh_error() {
|
|
||||||
return Err("SYSREF failed S/H timing");
|
|
||||||
}
|
|
||||||
let ret = unsafe { csr::sysref_sampler::sysref_phase_read() };
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: the code below assumes RTIO/SYSREF frequency ratio is a power of 2
|
|
||||||
|
|
||||||
fn sysref_get_tsc_phase() -> Result<i32, &'static str> {
|
|
||||||
let mask = (hmc7043::SYSREF_DIV/hmc7043::FPGA_CLK_DIV - 1) as u8;
|
|
||||||
Ok((sysref_get_tsc_phase_raw()? & mask) as i32)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_sysref_frequency() -> Result<(), &'static str> {
|
|
||||||
info!("testing SYSREF frequency against raw TSC phase bit toggles...");
|
|
||||||
|
|
||||||
let mut all_toggles = 0;
|
|
||||||
let initial_phase = sysref_get_tsc_phase_raw()?;
|
|
||||||
for _ in 0..20000 {
|
|
||||||
clock::spin_us(1);
|
|
||||||
all_toggles |= sysref_get_tsc_phase_raw()? ^ initial_phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
let ratio = (hmc7043::SYSREF_DIV/hmc7043::FPGA_CLK_DIV) as u8;
|
|
||||||
let expected_toggles = 0xff ^ (ratio - 1);
|
|
||||||
if all_toggles == expected_toggles {
|
|
||||||
info!(" ...done (0x{:02x})", all_toggles);
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
error!(" ...unexpected toggles: got 0x{:02x}, expected 0x{:02x}",
|
|
||||||
all_toggles, expected_toggles);
|
|
||||||
Err("unexpected toggles")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_slip_rtio_cycle() {
|
|
||||||
for _ in 0..hmc7043::FPGA_CLK_DIV {
|
|
||||||
hmc7043::sysref_slip();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_slip_tsc() -> Result<(), &'static str> {
|
|
||||||
info!("testing HMC7043 SYSREF slip against TSC phase...");
|
|
||||||
let initial_phase = sysref_get_tsc_phase()?;
|
|
||||||
let modulo = (hmc7043::SYSREF_DIV/hmc7043::FPGA_CLK_DIV) as i32;
|
|
||||||
for i in 0..128 {
|
|
||||||
sysref_slip_rtio_cycle();
|
|
||||||
let expected_phase = (initial_phase + i + 1) % modulo;
|
|
||||||
let phase = sysref_get_tsc_phase()?;
|
|
||||||
if phase != expected_phase {
|
|
||||||
error!(" ...unexpected TSC phase: got {}, expected {} ", phase, expected_phase);
|
|
||||||
return Err("HMC7043 SYSREF slip produced unexpected TSC phase");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
info!(" ...done");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_rtio_align() -> Result<(), &'static str> {
|
|
||||||
info!("aligning SYSREF with RTIO TSC...");
|
|
||||||
let mut nslips = 0;
|
|
||||||
loop {
|
|
||||||
sysref_slip_rtio_cycle();
|
|
||||||
if sysref_get_tsc_phase()? == 0 {
|
|
||||||
info!(" ...done");
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
nslips += 1;
|
|
||||||
if nslips > hmc7043::SYSREF_DIV/hmc7043::FPGA_CLK_DIV {
|
|
||||||
return Err("failed to find SYSREF transition aligned with RTIO TSC");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_auto_rtio_align() -> Result<(), &'static str> {
|
|
||||||
init_ddmtd()?;
|
|
||||||
test_ddmtd_stability(true, 4)?;
|
|
||||||
test_ddmtd_stability(false, 1)?;
|
|
||||||
test_slip_ddmtd()?;
|
|
||||||
|
|
||||||
info!("determining SYSREF S/H limits...");
|
|
||||||
let sysref_sh_limits = measure_sysref_sh_limits()?;
|
|
||||||
let rising_average = average_phases(&sysref_sh_limits.rising_phases, SYSREF_SH_MOD);
|
|
||||||
let falling_average = average_phases(&sysref_sh_limits.falling_phases, SYSREF_SH_MOD);
|
|
||||||
let rising_max_deviation = max_phase_deviation(rising_average, &sysref_sh_limits.rising_phases);
|
|
||||||
let falling_max_deviation = max_phase_deviation(falling_average, &sysref_sh_limits.falling_phases);
|
|
||||||
|
|
||||||
let rising_average = rising_average >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
let falling_average = falling_average >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
let rising_max_deviation = rising_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
let falling_max_deviation = falling_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
|
|
||||||
|
|
||||||
info!(" SYSREF S/H average limits (DDMTD phases): {} {}", rising_average, falling_average);
|
|
||||||
info!(" SYSREF S/H maximum limit deviation: {} {}", rising_max_deviation, falling_max_deviation);
|
|
||||||
if rising_max_deviation > 8 || falling_max_deviation > 8 {
|
|
||||||
return Err("excessive SYSREF S/H limit deviation");
|
|
||||||
}
|
|
||||||
info!(" ...done");
|
|
||||||
|
|
||||||
let entry = config::read_str("sysref_ddmtd_phase_fpga", |r| r.map(|s| s.parse()));
|
|
||||||
let target_phase = match entry {
|
|
||||||
Ok(Ok(phase)) => {
|
|
||||||
info!("using FPGA SYSREF DDMTD phase target from config: {}", phase);
|
|
||||||
phase
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let phase = calibrate_sysref_target(rising_average, falling_average)?;
|
|
||||||
if let Err(e) = config::write_int("sysref_ddmtd_phase_fpga", phase as u32) {
|
|
||||||
error!("failed to update FPGA SYSREF DDMTD phase target in config: {}", e);
|
|
||||||
}
|
|
||||||
phase
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
info!("aligning SYSREF with RTIO clock...");
|
|
||||||
let delta = reach_sysref_ddmtd_target(target_phase, 3)?;
|
|
||||||
if sysref_sh_error() {
|
|
||||||
return Err("SYSREF does not meet S/H timing at DDMTD phase target");
|
|
||||||
}
|
|
||||||
info!(" ...done, delta={}", delta);
|
|
||||||
|
|
||||||
test_sysref_frequency()?;
|
|
||||||
test_slip_tsc()?;
|
|
||||||
sysref_rtio_align()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_cal_dac(dacno: u8) -> Result<u8, &'static str> {
|
|
||||||
info!("calibrating SYSREF delay at DAC-{}...", dacno);
|
|
||||||
|
|
||||||
// Allocate for more than expected as jitter may create spurious entries.
|
|
||||||
let mut limits_buf = [0; 8];
|
|
||||||
let mut n_limits = 0;
|
|
||||||
|
|
||||||
limits_buf[n_limits] = -1;
|
|
||||||
n_limits += 1;
|
|
||||||
|
|
||||||
// avoid spurious rotation at delay=0
|
|
||||||
hmc7043::sysref_delay_dac(dacno, 0);
|
|
||||||
ad9154::dac_sync(dacno)?;
|
|
||||||
|
|
||||||
for scan_delay in 0..hmc7043::ANALOG_DELAY_RANGE {
|
|
||||||
hmc7043::sysref_delay_dac(dacno, scan_delay);
|
|
||||||
if ad9154::dac_sync(dacno)? {
|
|
||||||
limits_buf[n_limits] = scan_delay as i16;
|
|
||||||
n_limits += 1;
|
|
||||||
if n_limits >= limits_buf.len() - 1 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
limits_buf[n_limits] = hmc7043::ANALOG_DELAY_RANGE as i16;
|
|
||||||
n_limits += 1;
|
|
||||||
|
|
||||||
info!(" using limits: {:?}", &limits_buf[..n_limits]);
|
|
||||||
|
|
||||||
let mut delay = 0;
|
|
||||||
let mut best_margin = 0;
|
|
||||||
|
|
||||||
for i in 0..(n_limits-1) {
|
|
||||||
let margin = limits_buf[i+1] - limits_buf[i];
|
|
||||||
if margin > best_margin {
|
|
||||||
best_margin = margin;
|
|
||||||
delay = ((limits_buf[i+1] + limits_buf[i])/2) as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...done, delay={}", delay);
|
|
||||||
Ok(delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sysref_dac_align(dacno: u8, delay: u8) -> Result<(), &'static str> {
|
|
||||||
let tolerance = 5;
|
|
||||||
|
|
||||||
info!("verifying SYSREF margins at DAC-{}...", dacno);
|
|
||||||
|
|
||||||
// avoid spurious rotation at delay=0
|
|
||||||
hmc7043::sysref_delay_dac(dacno, 0);
|
|
||||||
ad9154::dac_sync(dacno)?;
|
|
||||||
|
|
||||||
let mut rotation_seen = false;
|
|
||||||
for scan_delay in 0..hmc7043::ANALOG_DELAY_RANGE {
|
|
||||||
hmc7043::sysref_delay_dac(dacno, scan_delay);
|
|
||||||
if ad9154::dac_sync(dacno)? {
|
|
||||||
rotation_seen = true;
|
|
||||||
let distance = (scan_delay as i16 - delay as i16).abs();
|
|
||||||
if distance < tolerance {
|
|
||||||
error!(" rotation at delay={} is {} delay steps from target (FAIL)", scan_delay, distance);
|
|
||||||
return Err("insufficient SYSREF margin at DAC");
|
|
||||||
} else {
|
|
||||||
info!(" rotation at delay={} is {} delay steps from target (PASS)", scan_delay, distance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rotation_seen {
|
|
||||||
return Err("no rotation seen when scanning DAC SYSREF delay");
|
|
||||||
}
|
|
||||||
|
|
||||||
info!(" ...done");
|
|
||||||
|
|
||||||
// We tested that the value is correct - now use it
|
|
||||||
hmc7043::sysref_delay_dac(dacno, delay);
|
|
||||||
ad9154::dac_sync(dacno)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sysref_auto_dac_align() -> Result<(), &'static str> {
|
|
||||||
// We assume that DAC SYSREF traces are length-matched so only one delay
|
|
||||||
// value is needed, and we use DAC-0 as calibration reference.
|
|
||||||
|
|
||||||
let entry = config::read_str("sysref_7043_delay_dac", |r| r.map(|s| s.parse()));
|
|
||||||
let delay = match entry {
|
|
||||||
Ok(Ok(delay)) => {
|
|
||||||
info!("using DAC SYSREF delay from config: {}", delay);
|
|
||||||
delay
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
let delay = sysref_cal_dac(0)?;
|
|
||||||
if let Err(e) = config::write_int("sysref_7043_delay_dac", delay as u32) {
|
|
||||||
error!("failed to update DAC SYSREF delay in config: {}", e);
|
|
||||||
}
|
|
||||||
delay
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for dacno in 0..csr::AD9154.len() {
|
|
||||||
sysref_dac_align(dacno as u8, delay)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -40,8 +40,6 @@ pub mod hmc830_7043;
|
||||||
mod ad9154_reg;
|
mod ad9154_reg;
|
||||||
#[cfg(has_ad9154)]
|
#[cfg(has_ad9154)]
|
||||||
pub mod ad9154;
|
pub mod ad9154;
|
||||||
/* TODO: #[cfg(has_jdcg)]
|
|
||||||
pub mod jesd204sync; */
|
|
||||||
#[cfg(has_allaki_atts)]
|
#[cfg(has_allaki_atts)]
|
||||||
pub mod hmc542;
|
pub mod hmc542;
|
||||||
|
|
||||||
|
|
|
@ -54,8 +54,8 @@ pub enum Packet {
|
||||||
SpiReadReply { succeeded: bool, data: u32 },
|
SpiReadReply { succeeded: bool, data: u32 },
|
||||||
SpiBasicReply { succeeded: bool },
|
SpiBasicReply { succeeded: bool },
|
||||||
|
|
||||||
JdacBasicRequest { destination: u8, dacno: u8, reqno: u8 },
|
JdacBasicRequest { destination: u8, dacno: u8, reqno: u8, param: u8 },
|
||||||
JdacBasicReply { succeeded: bool },
|
JdacBasicReply { succeeded: bool, retval: u8 },
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Packet {
|
impl Packet {
|
||||||
|
@ -185,9 +185,11 @@ impl Packet {
|
||||||
destination: reader.read_u8()?,
|
destination: reader.read_u8()?,
|
||||||
dacno: reader.read_u8()?,
|
dacno: reader.read_u8()?,
|
||||||
reqno: reader.read_u8()?,
|
reqno: reader.read_u8()?,
|
||||||
|
param: reader.read_u8()?,
|
||||||
},
|
},
|
||||||
0xa1 => Packet::JdacBasicReply {
|
0xa1 => Packet::JdacBasicReply {
|
||||||
succeeded: reader.read_bool()?
|
succeeded: reader.read_bool()?,
|
||||||
|
retval: reader.read_u8()?
|
||||||
},
|
},
|
||||||
|
|
||||||
ty => return Err(Error::UnknownPacket(ty))
|
ty => return Err(Error::UnknownPacket(ty))
|
||||||
|
@ -342,15 +344,17 @@ impl Packet {
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
},
|
},
|
||||||
|
|
||||||
Packet::JdacBasicRequest { destination, dacno, reqno } => {
|
Packet::JdacBasicRequest { destination, dacno, reqno, param } => {
|
||||||
writer.write_u8(0xa0)?;
|
writer.write_u8(0xa0)?;
|
||||||
writer.write_u8(destination)?;
|
writer.write_u8(destination)?;
|
||||||
writer.write_u8(dacno)?;
|
writer.write_u8(dacno)?;
|
||||||
writer.write_u8(reqno)?;
|
writer.write_u8(reqno)?;
|
||||||
|
writer.write_u8(param)?;
|
||||||
}
|
}
|
||||||
Packet::JdacBasicReply { succeeded } => {
|
Packet::JdacBasicReply { succeeded, retval } => {
|
||||||
writer.write_u8(0xa1)?;
|
writer.write_u8(0xa1)?;
|
||||||
writer.write_bool(succeeded)?;
|
writer.write_bool(succeeded)?;
|
||||||
|
writer.write_u8(retval)?;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -2,3 +2,7 @@ pub const INIT: u8 = 0x00;
|
||||||
pub const PRINT_STATUS: u8 = 0x01;
|
pub const PRINT_STATUS: u8 = 0x01;
|
||||||
pub const PRBS: u8 = 0x02;
|
pub const PRBS: u8 = 0x02;
|
||||||
pub const STPL: u8 = 0x03;
|
pub const STPL: u8 = 0x03;
|
||||||
|
|
||||||
|
pub const SYSREF_DELAY_DAC: u8 = 0x10;
|
||||||
|
pub const SYSREF_SLIP: u8 = 0x11;
|
||||||
|
pub const SYNC: u8 = 0x12;
|
||||||
|
|
|
@ -48,25 +48,37 @@ pub mod jdac {
|
||||||
use super::jesd;
|
use super::jesd;
|
||||||
use super::super::jdac_requests;
|
use super::super::jdac_requests;
|
||||||
|
|
||||||
pub fn basic_request(dacno: u8, reqno: u8) {
|
pub fn basic_request(dacno: u8, reqno: u8, param: u8) -> Result<u8, &'static str> {
|
||||||
if let Err(e) = drtioaux::send(1, &drtioaux::Packet::JdacBasicRequest {
|
if let Err(e) = drtioaux::send(1, &drtioaux::Packet::JdacBasicRequest {
|
||||||
destination: 0,
|
destination: 0,
|
||||||
dacno: dacno,
|
dacno: dacno,
|
||||||
reqno: reqno
|
reqno: reqno,
|
||||||
|
param: param
|
||||||
}) {
|
}) {
|
||||||
error!("aux packet error ({})", e);
|
error!("aux packet error ({})", e);
|
||||||
|
return Err("aux packet error while sending for JESD DAC basic request");
|
||||||
}
|
}
|
||||||
match drtioaux::recv_timeout(1, Some(1000)) {
|
match drtioaux::recv_timeout(1, Some(1000)) {
|
||||||
Ok(drtioaux::Packet::JdacBasicReply { succeeded }) =>
|
Ok(drtioaux::Packet::JdacBasicReply { succeeded, retval }) => {
|
||||||
if !succeeded {
|
if succeeded {
|
||||||
|
Ok(retval)
|
||||||
|
} else {
|
||||||
error!("JESD DAC basic request failed (dacno={}, reqno={})", dacno, reqno);
|
error!("JESD DAC basic request failed (dacno={}, reqno={})", dacno, reqno);
|
||||||
},
|
Err("remote error status to JESD DAC basic request")
|
||||||
Ok(packet) => error!("received unexpected aux packet: {:?}", packet),
|
}
|
||||||
Err(e) => error!("aux packet error ({})", e),
|
},
|
||||||
|
Ok(packet) => {
|
||||||
|
error!("received unexpected aux packet: {:?}", packet);
|
||||||
|
Err("unexpected aux packet in reply to JESD DAC basic request")
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
error!("aux packet error ({})", e);
|
||||||
|
Err("aux packet error while waiting for JESD DAC basic reply")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() -> Result<(), &'static str> {
|
||||||
for dacno in 0..csr::JDCG.len() {
|
for dacno in 0..csr::JDCG.len() {
|
||||||
let dacno = dacno as u8;
|
let dacno = dacno as u8;
|
||||||
info!("DAC-{} initializing...", dacno);
|
info!("DAC-{} initializing...", dacno);
|
||||||
|
@ -75,40 +87,513 @@ pub mod jdac {
|
||||||
clock::spin_us(10);
|
clock::spin_us(10);
|
||||||
if !jesd::ready(dacno) {
|
if !jesd::ready(dacno) {
|
||||||
error!("JESD core reported not ready");
|
error!("JESD core reported not ready");
|
||||||
|
return Err("JESD core reported not ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_request(dacno, jdac_requests::INIT);
|
basic_request(dacno, jdac_requests::INIT, 0)?;
|
||||||
|
|
||||||
jesd::prbs(dacno, true);
|
jesd::prbs(dacno, true);
|
||||||
basic_request(dacno, jdac_requests::PRBS);
|
basic_request(dacno, jdac_requests::PRBS, 0)?;
|
||||||
jesd::prbs(dacno, false);
|
jesd::prbs(dacno, false);
|
||||||
|
|
||||||
jesd::stpl(dacno, true);
|
jesd::stpl(dacno, true);
|
||||||
basic_request(dacno, jdac_requests::STPL);
|
basic_request(dacno, jdac_requests::STPL, 0)?;
|
||||||
jesd::stpl(dacno, false);
|
jesd::stpl(dacno, false);
|
||||||
|
|
||||||
basic_request(dacno, jdac_requests::INIT);
|
basic_request(dacno, jdac_requests::INIT, 0)?;
|
||||||
clock::spin_us(5000);
|
clock::spin_us(5000);
|
||||||
|
|
||||||
basic_request(dacno, jdac_requests::PRINT_STATUS);
|
basic_request(dacno, jdac_requests::PRINT_STATUS, 0)?;
|
||||||
|
|
||||||
if !jesd::jsync(dacno) {
|
if !jesd::jsync(dacno) {
|
||||||
error!("JESD core reported bad SYNC");
|
error!("JESD core reported bad SYNC");
|
||||||
|
return Err("JESD core reported bad SYNC");
|
||||||
}
|
}
|
||||||
|
|
||||||
info!(" ...done");
|
info!(" ...done");
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod jesd204sync {
|
pub mod jesd204sync {
|
||||||
fn sysref_auto_rtio_align() -> Result<(), &'static str> {
|
use board_misoc::{csr, clock, config};
|
||||||
info!("TODO: sysref_auto_rtio_align");
|
|
||||||
|
use super::jdac;
|
||||||
|
use super::super::jdac_requests;
|
||||||
|
|
||||||
|
const HMC7043_ANALOG_DELAY_RANGE: u8 = 24;
|
||||||
|
|
||||||
|
const FPGA_CLK_DIV: u16 = 16; // Keep in sync with hmc830_7043.rs
|
||||||
|
const SYSREF_DIV: u16 = 256; // Keep in sync with hmc830_7043.rs
|
||||||
|
|
||||||
|
fn hmc7043_sysref_delay_dac(dacno: u8, phase_offset: u8) -> Result<(), &'static str> {
|
||||||
|
match jdac::basic_request(dacno, jdac_requests::SYSREF_DELAY_DAC, phase_offset) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn hmc7043_sysref_slip() -> Result<(), &'static str> {
|
||||||
|
match jdac::basic_request(0, jdac_requests::SYSREF_SLIP, 0) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ad9154_sync(dacno: u8) -> Result<bool, &'static str> {
|
||||||
|
match jdac::basic_request(dacno, jdac_requests::SYNC, 0) {
|
||||||
|
Ok(0) => Ok(false),
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(e) => Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn average_2phases(a: i32, b: i32, modulo: i32) -> i32 {
|
||||||
|
let diff = ((a - b + modulo/2 + modulo) % modulo) - modulo/2;
|
||||||
|
return (modulo + b + diff/2) % modulo;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn average_phases(phases: &[i32], modulo: i32) -> i32 {
|
||||||
|
if phases.len() == 1 {
|
||||||
|
panic!("input array length must be a power of 2");
|
||||||
|
} else if phases.len() == 2 {
|
||||||
|
average_2phases(phases[0], phases[1], modulo)
|
||||||
|
} else {
|
||||||
|
let cut = phases.len()/2;
|
||||||
|
average_2phases(
|
||||||
|
average_phases(&phases[..cut], modulo),
|
||||||
|
average_phases(&phases[cut..], modulo),
|
||||||
|
modulo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RAW_DDMTD_N_SHIFT: i32 = 6;
|
||||||
|
const RAW_DDMTD_N: i32 = 1 << RAW_DDMTD_N_SHIFT;
|
||||||
|
const DDMTD_DITHER_BITS: i32 = 1;
|
||||||
|
const DDMTD_N_SHIFT: i32 = RAW_DDMTD_N_SHIFT + DDMTD_DITHER_BITS;
|
||||||
|
const DDMTD_N: i32 = 1 << DDMTD_N_SHIFT;
|
||||||
|
|
||||||
|
fn init_ddmtd() -> Result<(), &'static str> {
|
||||||
|
unsafe {
|
||||||
|
csr::sysref_ddmtd::reset_write(1);
|
||||||
|
clock::spin_us(1);
|
||||||
|
csr::sysref_ddmtd::reset_write(0);
|
||||||
|
clock::spin_us(100);
|
||||||
|
if csr::sysref_ddmtd::locked_read() != 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err("DDMTD helper PLL failed to lock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn measure_ddmdt_phase_raw() -> i32 {
|
||||||
|
unsafe { csr::sysref_ddmtd::dt_read() as i32 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn measure_ddmdt_phase() -> i32 {
|
||||||
|
const AVG_PRECISION_SHIFT: i32 = 6;
|
||||||
|
const AVG_PRECISION: i32 = 1 << AVG_PRECISION_SHIFT;
|
||||||
|
const AVG_MOD: i32 = 1 << (RAW_DDMTD_N_SHIFT + AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
|
||||||
|
|
||||||
|
let mut measurements = [0; AVG_PRECISION as usize];
|
||||||
|
for i in 0..AVG_PRECISION {
|
||||||
|
measurements[i as usize] = measure_ddmdt_phase_raw() << (AVG_PRECISION_SHIFT + DDMTD_DITHER_BITS);
|
||||||
|
clock::spin_us(10);
|
||||||
|
}
|
||||||
|
average_phases(&measurements, AVG_MOD) >> AVG_PRECISION_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ddmtd_stability(raw: bool, tolerance: i32) -> Result<(), &'static str> {
|
||||||
|
info!("testing DDMTD stability (raw={}, tolerance={})...", raw, tolerance);
|
||||||
|
|
||||||
|
let modulo = if raw { RAW_DDMTD_N } else { DDMTD_N };
|
||||||
|
let measurement = if raw { measure_ddmdt_phase_raw } else { measure_ddmdt_phase };
|
||||||
|
let ntests = if raw { 15000 } else { 150 };
|
||||||
|
|
||||||
|
let mut max_pkpk = 0;
|
||||||
|
for _ in 0..32 {
|
||||||
|
// If we are near the edges, wraparound can throw off the simple min/max computation.
|
||||||
|
// In this case, add an offset to get near the center.
|
||||||
|
let quadrant = measure_ddmdt_phase();
|
||||||
|
let center_offset =
|
||||||
|
if quadrant < DDMTD_N/4 || quadrant > 3*DDMTD_N/4 {
|
||||||
|
modulo/2
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut min = modulo;
|
||||||
|
let mut max = 0;
|
||||||
|
for _ in 0..ntests {
|
||||||
|
let m = (measurement() + center_offset) % modulo;
|
||||||
|
if m < min {
|
||||||
|
min = m;
|
||||||
|
}
|
||||||
|
if m > max {
|
||||||
|
max = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let pkpk = max - min;
|
||||||
|
if pkpk > max_pkpk {
|
||||||
|
max_pkpk = pkpk;
|
||||||
|
}
|
||||||
|
if pkpk > tolerance {
|
||||||
|
error!(" ...excessive peak-peak jitter: {} (min={} max={} center_offset={})", pkpk,
|
||||||
|
min, max, center_offset);
|
||||||
|
return Err("excessive DDMTD peak-peak jitter");
|
||||||
|
}
|
||||||
|
hmc7043_sysref_slip();
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(" ...passed, peak-peak jitter: {}", max_pkpk);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sysref_auto_dac_align() -> Result<(), &'static str> {
|
fn test_slip_ddmtd() -> Result<(), &'static str> {
|
||||||
info!("TODO: sysref_auto_dac_align");
|
// expected_step = (RTIO clock frequency)*(DDMTD N)/(HMC7043 CLKIN frequency)
|
||||||
|
let expected_step = 8;
|
||||||
|
let tolerance = 1;
|
||||||
|
|
||||||
|
info!("testing HMC7043 SYSREF slip against DDMTD...");
|
||||||
|
let mut old_phase = measure_ddmdt_phase();
|
||||||
|
for _ in 0..1024 {
|
||||||
|
hmc7043_sysref_slip();
|
||||||
|
let phase = measure_ddmdt_phase();
|
||||||
|
let step = (DDMTD_N + old_phase - phase) % DDMTD_N;
|
||||||
|
if (step - expected_step).abs() > tolerance {
|
||||||
|
error!(" ...got unexpected step: {} ({} -> {})", step, old_phase, phase);
|
||||||
|
return Err("HMC7043 SYSREF slip produced unexpected DDMTD step");
|
||||||
|
}
|
||||||
|
old_phase = phase;
|
||||||
|
}
|
||||||
|
info!(" ...passed");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sysref_sh_error() -> bool {
|
||||||
|
unsafe {
|
||||||
|
csr::sysref_sampler::sh_error_reset_write(1);
|
||||||
|
clock::spin_us(1);
|
||||||
|
csr::sysref_sampler::sh_error_reset_write(0);
|
||||||
|
clock::spin_us(10);
|
||||||
|
csr::sysref_sampler::sh_error_read() != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SYSREF_SH_PRECISION_SHIFT: i32 = 5;
|
||||||
|
const SYSREF_SH_PRECISION: i32 = 1 << SYSREF_SH_PRECISION_SHIFT;
|
||||||
|
const SYSREF_SH_MOD: i32 = 1 << (DDMTD_N_SHIFT + SYSREF_SH_PRECISION_SHIFT);
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct SysrefShLimits {
|
||||||
|
rising_phases: [i32; SYSREF_SH_PRECISION as usize],
|
||||||
|
falling_phases: [i32; SYSREF_SH_PRECISION as usize],
|
||||||
|
}
|
||||||
|
|
||||||
|
fn measure_sysref_sh_limits() -> Result<SysrefShLimits, &'static str> {
|
||||||
|
let mut ret = SysrefShLimits::default();
|
||||||
|
let mut nslips = 0;
|
||||||
|
let mut rising_n = 0;
|
||||||
|
let mut falling_n = 0;
|
||||||
|
|
||||||
|
let mut previous = sysref_sh_error();
|
||||||
|
while rising_n < SYSREF_SH_PRECISION || falling_n < SYSREF_SH_PRECISION {
|
||||||
|
hmc7043_sysref_slip();
|
||||||
|
nslips += 1;
|
||||||
|
if nslips > 1024 {
|
||||||
|
return Err("too many slips and not enough SYSREF S/H error transitions");
|
||||||
|
}
|
||||||
|
|
||||||
|
let current = sysref_sh_error();
|
||||||
|
let phase = measure_ddmdt_phase();
|
||||||
|
if current && !previous && rising_n < SYSREF_SH_PRECISION {
|
||||||
|
ret.rising_phases[rising_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
|
||||||
|
rising_n += 1;
|
||||||
|
}
|
||||||
|
if !current && previous && falling_n < SYSREF_SH_PRECISION {
|
||||||
|
ret.falling_phases[falling_n as usize] = phase << SYSREF_SH_PRECISION_SHIFT;
|
||||||
|
falling_n += 1;
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn max_phase_deviation(average: i32, phases: &[i32]) -> i32 {
|
||||||
|
let mut ret = 0;
|
||||||
|
for phase in phases.iter() {
|
||||||
|
let deviation = (phase - average + DDMTD_N) % DDMTD_N;
|
||||||
|
if deviation > ret {
|
||||||
|
ret = deviation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reach_sysref_ddmtd_target(target: i32, tolerance: i32) -> Result<i32, &'static str> {
|
||||||
|
for _ in 0..1024 {
|
||||||
|
let delta = (measure_ddmdt_phase() - target + DDMTD_N) % DDMTD_N;
|
||||||
|
if delta <= tolerance {
|
||||||
|
return Ok(delta)
|
||||||
|
}
|
||||||
|
hmc7043_sysref_slip();
|
||||||
|
}
|
||||||
|
Err("failed to reach SYSREF DDMTD phase target")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calibrate_sysref_target(rising_average: i32, falling_average: i32) -> Result<i32, &'static str> {
|
||||||
|
info!("calibrating SYSREF DDMTD target phase...");
|
||||||
|
let coarse_target =
|
||||||
|
if rising_average < falling_average {
|
||||||
|
(rising_average + falling_average)/2
|
||||||
|
} else {
|
||||||
|
((falling_average - (DDMTD_N - rising_average))/2 + DDMTD_N) % DDMTD_N
|
||||||
|
};
|
||||||
|
info!(" SYSREF calibration coarse target: {}", coarse_target);
|
||||||
|
reach_sysref_ddmtd_target(coarse_target, 8)?;
|
||||||
|
let target = measure_ddmdt_phase();
|
||||||
|
info!(" ...done, target={}", target);
|
||||||
|
Ok(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sysref_get_tsc_phase_raw() -> Result<u8, &'static str> {
|
||||||
|
if sysref_sh_error() {
|
||||||
|
return Err("SYSREF failed S/H timing");
|
||||||
|
}
|
||||||
|
let ret = unsafe { csr::sysref_sampler::sysref_phase_read() };
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: the code below assumes RTIO/SYSREF frequency ratio is a power of 2
|
||||||
|
|
||||||
|
fn sysref_get_tsc_phase() -> Result<i32, &'static str> {
|
||||||
|
let mask = (SYSREF_DIV/FPGA_CLK_DIV - 1) as u8;
|
||||||
|
Ok((sysref_get_tsc_phase_raw()? & mask) as i32)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_sysref_frequency() -> Result<(), &'static str> {
|
||||||
|
info!("testing SYSREF frequency against raw TSC phase bit toggles...");
|
||||||
|
|
||||||
|
let mut all_toggles = 0;
|
||||||
|
let initial_phase = sysref_get_tsc_phase_raw()?;
|
||||||
|
for _ in 0..20000 {
|
||||||
|
clock::spin_us(1);
|
||||||
|
all_toggles |= sysref_get_tsc_phase_raw()? ^ initial_phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ratio = (SYSREF_DIV/FPGA_CLK_DIV) as u8;
|
||||||
|
let expected_toggles = 0xff ^ (ratio - 1);
|
||||||
|
if all_toggles == expected_toggles {
|
||||||
|
info!(" ...done (0x{:02x})", all_toggles);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
error!(" ...unexpected toggles: got 0x{:02x}, expected 0x{:02x}",
|
||||||
|
all_toggles, expected_toggles);
|
||||||
|
Err("unexpected toggles")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sysref_slip_rtio_cycle() {
|
||||||
|
for _ in 0..FPGA_CLK_DIV {
|
||||||
|
hmc7043_sysref_slip();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_slip_tsc() -> Result<(), &'static str> {
|
||||||
|
info!("testing HMC7043 SYSREF slip against TSC phase...");
|
||||||
|
let initial_phase = sysref_get_tsc_phase()?;
|
||||||
|
let modulo = (SYSREF_DIV/FPGA_CLK_DIV) as i32;
|
||||||
|
for i in 0..128 {
|
||||||
|
sysref_slip_rtio_cycle();
|
||||||
|
let expected_phase = (initial_phase + i + 1) % modulo;
|
||||||
|
let phase = sysref_get_tsc_phase()?;
|
||||||
|
if phase != expected_phase {
|
||||||
|
error!(" ...unexpected TSC phase: got {}, expected {} ", phase, expected_phase);
|
||||||
|
return Err("HMC7043 SYSREF slip produced unexpected TSC phase");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!(" ...done");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sysref_rtio_align() -> Result<(), &'static str> {
|
||||||
|
info!("aligning SYSREF with RTIO TSC...");
|
||||||
|
let mut nslips = 0;
|
||||||
|
loop {
|
||||||
|
sysref_slip_rtio_cycle();
|
||||||
|
if sysref_get_tsc_phase()? == 0 {
|
||||||
|
info!(" ...done");
|
||||||
|
return Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
nslips += 1;
|
||||||
|
if nslips > SYSREF_DIV/FPGA_CLK_DIV {
|
||||||
|
return Err("failed to find SYSREF transition aligned with RTIO TSC");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sysref_auto_rtio_align() -> Result<(), &'static str> {
|
||||||
|
init_ddmtd()?;
|
||||||
|
test_ddmtd_stability(true, 4)?;
|
||||||
|
test_ddmtd_stability(false, 1)?;
|
||||||
|
test_slip_ddmtd()?;
|
||||||
|
|
||||||
|
info!("determining SYSREF S/H limits...");
|
||||||
|
let sysref_sh_limits = measure_sysref_sh_limits()?;
|
||||||
|
let rising_average = average_phases(&sysref_sh_limits.rising_phases, SYSREF_SH_MOD);
|
||||||
|
let falling_average = average_phases(&sysref_sh_limits.falling_phases, SYSREF_SH_MOD);
|
||||||
|
let rising_max_deviation = max_phase_deviation(rising_average, &sysref_sh_limits.rising_phases);
|
||||||
|
let falling_max_deviation = max_phase_deviation(falling_average, &sysref_sh_limits.falling_phases);
|
||||||
|
|
||||||
|
let rising_average = rising_average >> SYSREF_SH_PRECISION_SHIFT;
|
||||||
|
let falling_average = falling_average >> SYSREF_SH_PRECISION_SHIFT;
|
||||||
|
let rising_max_deviation = rising_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
|
||||||
|
let falling_max_deviation = falling_max_deviation >> SYSREF_SH_PRECISION_SHIFT;
|
||||||
|
|
||||||
|
info!(" SYSREF S/H average limits (DDMTD phases): {} {}", rising_average, falling_average);
|
||||||
|
info!(" SYSREF S/H maximum limit deviation: {} {}", rising_max_deviation, falling_max_deviation);
|
||||||
|
if rising_max_deviation > 8 || falling_max_deviation > 8 {
|
||||||
|
return Err("excessive SYSREF S/H limit deviation");
|
||||||
|
}
|
||||||
|
info!(" ...done");
|
||||||
|
|
||||||
|
let entry = config::read_str("sysref_ddmtd_phase_fpga", |r| r.map(|s| s.parse()));
|
||||||
|
let target_phase = match entry {
|
||||||
|
Ok(Ok(phase)) => {
|
||||||
|
info!("using FPGA SYSREF DDMTD phase target from config: {}", phase);
|
||||||
|
phase
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let phase = calibrate_sysref_target(rising_average, falling_average)?;
|
||||||
|
if let Err(e) = config::write_int("sysref_ddmtd_phase_fpga", phase as u32) {
|
||||||
|
error!("failed to update FPGA SYSREF DDMTD phase target in config: {}", e);
|
||||||
|
}
|
||||||
|
phase
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("aligning SYSREF with RTIO clock...");
|
||||||
|
let delta = reach_sysref_ddmtd_target(target_phase, 3)?;
|
||||||
|
if sysref_sh_error() {
|
||||||
|
return Err("SYSREF does not meet S/H timing at DDMTD phase target");
|
||||||
|
}
|
||||||
|
info!(" ...done, delta={}", delta);
|
||||||
|
|
||||||
|
test_sysref_frequency()?;
|
||||||
|
test_slip_tsc()?;
|
||||||
|
sysref_rtio_align()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sysref_cal_dac(dacno: u8) -> Result<u8, &'static str> {
|
||||||
|
info!("calibrating SYSREF delay at DAC-{}...", dacno);
|
||||||
|
|
||||||
|
// Allocate for more than expected as jitter may create spurious entries.
|
||||||
|
let mut limits_buf = [0; 8];
|
||||||
|
let mut n_limits = 0;
|
||||||
|
|
||||||
|
limits_buf[n_limits] = -1;
|
||||||
|
n_limits += 1;
|
||||||
|
|
||||||
|
// avoid spurious rotation at delay=0
|
||||||
|
hmc7043_sysref_delay_dac(dacno, 0);
|
||||||
|
ad9154_sync(dacno)?;
|
||||||
|
|
||||||
|
for scan_delay in 0..HMC7043_ANALOG_DELAY_RANGE {
|
||||||
|
hmc7043_sysref_delay_dac(dacno, scan_delay);
|
||||||
|
if ad9154_sync(dacno)? {
|
||||||
|
limits_buf[n_limits] = scan_delay as i16;
|
||||||
|
n_limits += 1;
|
||||||
|
if n_limits >= limits_buf.len() - 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
limits_buf[n_limits] = HMC7043_ANALOG_DELAY_RANGE as i16;
|
||||||
|
n_limits += 1;
|
||||||
|
|
||||||
|
info!(" using limits: {:?}", &limits_buf[..n_limits]);
|
||||||
|
|
||||||
|
let mut delay = 0;
|
||||||
|
let mut best_margin = 0;
|
||||||
|
|
||||||
|
for i in 0..(n_limits-1) {
|
||||||
|
let margin = limits_buf[i+1] - limits_buf[i];
|
||||||
|
if margin > best_margin {
|
||||||
|
best_margin = margin;
|
||||||
|
delay = ((limits_buf[i+1] + limits_buf[i])/2) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(" ...done, delay={}", delay);
|
||||||
|
Ok(delay)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sysref_dac_align(dacno: u8, delay: u8) -> Result<(), &'static str> {
|
||||||
|
let tolerance = 5;
|
||||||
|
|
||||||
|
info!("verifying SYSREF margins at DAC-{}...", dacno);
|
||||||
|
|
||||||
|
// avoid spurious rotation at delay=0
|
||||||
|
hmc7043_sysref_delay_dac(dacno, 0);
|
||||||
|
ad9154_sync(dacno)?;
|
||||||
|
|
||||||
|
let mut rotation_seen = false;
|
||||||
|
for scan_delay in 0..HMC7043_ANALOG_DELAY_RANGE {
|
||||||
|
hmc7043_sysref_delay_dac(dacno, scan_delay);
|
||||||
|
if ad9154_sync(dacno)? {
|
||||||
|
rotation_seen = true;
|
||||||
|
let distance = (scan_delay as i16 - delay as i16).abs();
|
||||||
|
if distance < tolerance {
|
||||||
|
error!(" rotation at delay={} is {} delay steps from target (FAIL)", scan_delay, distance);
|
||||||
|
return Err("insufficient SYSREF margin at DAC");
|
||||||
|
} else {
|
||||||
|
info!(" rotation at delay={} is {} delay steps from target (PASS)", scan_delay, distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rotation_seen {
|
||||||
|
return Err("no rotation seen when scanning DAC SYSREF delay");
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(" ...done");
|
||||||
|
|
||||||
|
// We tested that the value is correct - now use it
|
||||||
|
hmc7043_sysref_delay_dac(dacno, delay);
|
||||||
|
ad9154_sync(dacno)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sysref_auto_dac_align() -> Result<(), &'static str> {
|
||||||
|
// We assume that DAC SYSREF traces are length-matched so only one delay
|
||||||
|
// value is needed, and we use DAC-0 as calibration reference.
|
||||||
|
|
||||||
|
let entry = config::read_str("sysref_7043_delay_dac", |r| r.map(|s| s.parse()));
|
||||||
|
let delay = match entry {
|
||||||
|
Ok(Ok(delay)) => {
|
||||||
|
info!("using DAC SYSREF delay from config: {}", delay);
|
||||||
|
delay
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
let delay = sysref_cal_dac(0)?;
|
||||||
|
if let Err(e) = config::write_int("sysref_7043_delay_dac", delay as u32) {
|
||||||
|
error!("failed to update DAC SYSREF delay in config: {}", e);
|
||||||
|
}
|
||||||
|
delay
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for dacno in 0..csr::JDCG.len() {
|
||||||
|
sysref_dac_align(dacno as u8, delay)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -292,26 +292,39 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drtioaux::Packet::JdacBasicRequest { destination: _destination, dacno: _dacno, reqno: _reqno } => {
|
drtioaux::Packet::JdacBasicRequest { destination: _destination, dacno: _dacno,
|
||||||
|
reqno: _reqno, param: _param } => {
|
||||||
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
forward!(_routing_table, _destination, *_rank, _repeaters, &packet);
|
||||||
#[cfg(has_ad9154)]
|
#[cfg(has_ad9154)]
|
||||||
let succeeded = {
|
let (succeeded, retval) = {
|
||||||
#[cfg(rtio_frequency = "125.0")]
|
#[cfg(rtio_frequency = "125.0")]
|
||||||
const LINERATE: u64 = 5_000_000_000;
|
const LINERATE: u64 = 5_000_000_000;
|
||||||
#[cfg(rtio_frequency = "150.0")]
|
#[cfg(rtio_frequency = "150.0")]
|
||||||
const LINERATE: u64 = 6_000_000_000;
|
const LINERATE: u64 = 6_000_000_000;
|
||||||
match _reqno {
|
match _reqno {
|
||||||
jdac_requests::INIT => board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(),
|
jdac_requests::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0),
|
||||||
jdac_requests::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); true },
|
jdac_requests::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) },
|
||||||
jdac_requests::PRBS => board_artiq::ad9154::prbs(_dacno).is_ok(),
|
jdac_requests::PRBS => (board_artiq::ad9154::prbs(_dacno).is_ok(), 0),
|
||||||
jdac_requests::STPL => board_artiq::ad9154::stpl(_dacno, 4, 2).is_ok(),
|
jdac_requests::STPL => (board_artiq::ad9154::stpl(_dacno, 4, 2).is_ok(), 0),
|
||||||
_ => false
|
jdac_requests::SYSREF_DELAY_DAC => { board_artiq::hmc830_7043::hmc7043::sysref_delay_dac(_dacno, _param); (true, 0) },
|
||||||
|
jdac_requests::SYSREF_SLIP => { board_artiq::hmc830_7043::hmc7043::sysref_slip(); (true, 0) },
|
||||||
|
jdac_requests::SYNC => {
|
||||||
|
match board_artiq::ad9154::sync(_dacno) {
|
||||||
|
Ok(false) => (true, 0),
|
||||||
|
Ok(true) => (true, 1),
|
||||||
|
Err(e) => {
|
||||||
|
error!("DAC sync failed: {}", e);
|
||||||
|
(false, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (false, 0)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#[cfg(not(has_ad9154))]
|
#[cfg(not(has_ad9154))]
|
||||||
let succeeded = false;
|
let (succeeded, retval) = (false, 0);
|
||||||
drtioaux::send(0,
|
drtioaux::send(0,
|
||||||
&drtioaux::Packet::JdacBasicReply { succeeded: succeeded })
|
&drtioaux::Packet::JdacBasicReply { succeeded: succeeded, retval: retval })
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -500,7 +513,7 @@ pub extern fn main() -> i32 {
|
||||||
*/
|
*/
|
||||||
jdcg::jesd::reset(false);
|
jdcg::jesd::reset(false);
|
||||||
if repeaters[0].is_up() {
|
if repeaters[0].is_up() {
|
||||||
jdcg::jdac::init();
|
let _ = jdcg::jdac::init();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,7 +551,7 @@ pub extern fn main() -> i32 {
|
||||||
{
|
{
|
||||||
let rep0_is_up = repeaters[0].is_up();
|
let rep0_is_up = repeaters[0].is_up();
|
||||||
if rep0_is_up && !rep0_was_up {
|
if rep0_is_up && !rep0_was_up {
|
||||||
jdcg::jdac::init();
|
let _ = jdcg::jdac::init();
|
||||||
jdcg::jesd204sync::sysref_auto_align();
|
jdcg::jesd204sync::sysref_auto_align();
|
||||||
}
|
}
|
||||||
rep0_was_up = rep0_is_up;
|
rep0_was_up = rep0_is_up;
|
||||||
|
|
Loading…
Reference in New Issue