diff --git a/src/runtime/src/si5324.rs b/src/runtime/src/si5324.rs index 48ce207..dbd301d 100644 --- a/src/runtime/src/si5324.rs +++ b/src/runtime/src/si5324.rs @@ -271,3 +271,75 @@ pub fn select_input(i2c: &mut I2c, input: Input, timer: GlobalTimer) -> Result<( monitor_lock(i2c, timer)?; Ok(()) } + +#[cfg(has_siphaser)] +pub mod siphaser { + use super::*; + use pl::csr; + + pub fn select_recovered_clock(i2c: &mut I2c, rc: bool, timer: GlobalTimer) -> Result<()> { + write(i2c, 3, (read(3)? & 0xdf) | (1 << 5))?; // DHOLD=1 + unsafe { + csr::siphaser::switch_clocks_write(if rc { 1 } else { 0 }); + } + write(i2c, 3, (read(3)? & 0xdf) | (0 << 5))?; // DHOLD=0 + monitor_lock(timer)?; + Ok(()) + } + + fn phase_shift(direction: u8, timer: GlobalTimer) { + unsafe { + csr::siphaser::phase_shift_write(direction); + while csr::siphaser::phase_shift_done_read() == 0 {} + } + // wait for the Si5324 loop to stabilize + timer.delay_us(500); + } + + fn has_error(timer: GlobalTimer) -> bool { + unsafe { + csr::siphaser::error_write(1); + } + timer.delay_us(5_000); + unsafe { + csr::siphaser::error_read() != 0 + } + } + + fn find_edge(target: bool, timer: GlobalTimer) -> Result { + let mut nshifts = 0; + + let mut previous = has_error(timer); + loop { + phase_shift(1, timer); + nshifts += 1; + let current = has_error(timer); + if previous != target && current == target { + return Ok(nshifts); + } + if nshifts > 5000 { + return Err("failed to find timing error edge"); + } + previous = current; + } + } + + pub fn calibrate_skew(timer: GlobalTimer) -> Result<()> { + let jitter_margin = 32; + let lead = find_edge(false, timer)?; + for _ in 0..jitter_margin { + phase_shift(1, timer); + } + let width = find_edge(true, timer)? + jitter_margin; + // width is 360 degrees (one full rotation of the phase between s/h limits) minus jitter + info!("calibration successful, lead: {}, width: {} ({}deg)", lead, width, width*360/(56*8)); + + // Apply reverse phase shift for half the width to get into the + // middle of the working region. + for _ in 0..width/2 { + phase_shift(0, timer); + } + + Ok(()) + } +}