From 682e92b17e46deb71bab4dc420eb569963b4b863 Mon Sep 17 00:00:00 2001 From: morgan Date: Tue, 19 Mar 2024 10:36:47 +0800 Subject: [PATCH] Firmware: Satman skew calibration & tester cargo template: add calibrate_wrpll_skew feature tag collector: add TAG_OFFSET for Satman WRPLL tag collector: add TAG_OFFSET getter & setter for calibration wrpll: add skew tester and calibration wrpll: gate calibration behind calibrate_wrpll_skew feature --- src/libboard_artiq/Cargo.toml.tpl | 1 + src/libboard_artiq/src/si549.rs | 131 +++++++++++++++++++++++++++++- src/satman/Cargo.toml.tpl | 1 + 3 files changed, 131 insertions(+), 2 deletions(-) diff --git a/src/libboard_artiq/Cargo.toml.tpl b/src/libboard_artiq/Cargo.toml.tpl index 9c1ad24..995fc05 100644 --- a/src/libboard_artiq/Cargo.toml.tpl +++ b/src/libboard_artiq/Cargo.toml.tpl @@ -10,6 +10,7 @@ name = "libboard_artiq" [features] target_zc706 = ["libboard_zynq/target_zc706", "libconfig/target_zc706"] target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libconfig/target_kasli_soc"] +calibrate_wrpll_skew = [] [build-dependencies] build_zynq = { path = "../libbuild_zynq" } diff --git a/src/libboard_artiq/src/si549.rs b/src/libboard_artiq/src/si549.rs index 0c98045..6b0cb4d 100644 --- a/src/libboard_artiq/src/si549.rs +++ b/src/libboard_artiq/src/si549.rs @@ -357,6 +357,10 @@ pub mod wrpll { mod tag_collector { use super::*; + #[cfg(wrpll_ref_clk = "GT_CDR")] + static TAG_OFFSET: Mutex = Mutex::new(19050); + #[cfg(wrpll_ref_clk = "SMA_CLKIN")] + static TAG_OFFSET: Mutex = Mutex::new(0); static REF_TAG: Mutex = Mutex::new(0); static REF_TAG_READY: Mutex = Mutex::new(false); static MAIN_TAG: Mutex = Mutex::new(0); @@ -390,6 +394,16 @@ pub mod wrpll { *REF_TAG_READY.lock() && *MAIN_TAG_READY.lock() } + #[cfg(feature = "calibrate_wrpll_skew")] + pub fn set_tag_offset(offset: u32) { + *TAG_OFFSET.lock() = offset; + } + + #[cfg(feature = "calibrate_wrpll_skew")] + pub fn get_tag_offset() -> u32 { + *TAG_OFFSET.lock() + } + pub fn get_period_error() -> i32 { // n * BEATING_PERIOD - REF_TAG(n) mod BEATING_PERIOD let mut period_error = (*REF_TAG.lock()).overflowing_neg().0.rem_euclid(BEATING_PERIOD as u32) as i32; @@ -402,9 +416,9 @@ pub mod wrpll { } pub fn get_phase_error() -> i32 { - // MAIN_TAG(n) - REF_TAG(n) mod BEATING_PERIOD + // MAIN_TAG(n) - REF_TAG(n) - TAG_OFFSET mod BEATING_PERIOD let mut phase_error = (*MAIN_TAG.lock()) - .overflowing_sub(*REF_TAG.lock()) + .overflowing_sub(*REF_TAG.lock() + *TAG_OFFSET.lock()) .0 .rem_euclid(BEATING_PERIOD as u32) as i32; @@ -520,6 +534,113 @@ pub mod wrpll { Ok(()) } + #[cfg(wrpll_ref_clk = "GT_CDR")] + fn test_skew(timer: &mut GlobalTimer) -> Result<(), &'static str> { + // wait for PLL to stabilize + timer.delay_us(20_000); + + info!("testing the skew of SYS CLK..."); + if has_timing_error(timer) { + return Err("the skew cannot satisfy setup/hold time constraint of RX synchronizer"); + } + info!("the skew of SYS CLK met the timing constraint"); + Ok(()) + } + + #[cfg(wrpll_ref_clk = "GT_CDR")] + fn has_timing_error(timer: &mut GlobalTimer) -> bool { + unsafe { + csr::wrpll_skewtester::error_write(1); + } + timer.delay_us(5_000); + unsafe { csr::wrpll_skewtester::error_read() == 1 } + } + + #[cfg(feature = "calibrate_wrpll_skew")] + fn find_edge(target: bool, timer: &mut GlobalTimer) -> Result { + const STEP: u32 = 8; + const STABLE_THRESHOLD: u32 = 10; + + enum FSM { + Init, + WaitEdge, + GotEdge, + } + + let mut state: FSM = FSM::Init; + let mut offset: u32 = tag_collector::get_tag_offset(); + let mut median_edge: u32 = 0; + let mut stable_counter: u32 = 0; + + for _ in 0..(BEATING_PERIOD as u32 / STEP) as usize { + tag_collector::set_tag_offset(offset); + offset += STEP; + // wait for PLL to stabilize + timer.delay_us(20_000); + + let error = has_timing_error(timer); + // A median edge deglitcher + match state { + FSM::Init => { + if error != target { + stable_counter += 1; + } else { + stable_counter = 0; + } + + if stable_counter >= STABLE_THRESHOLD { + state = FSM::WaitEdge; + stable_counter = 0; + } + } + FSM::WaitEdge => { + if error == target { + state = FSM::GotEdge; + median_edge = offset; + } + } + FSM::GotEdge => { + if error != target { + median_edge += STEP; + stable_counter = 0; + } else { + stable_counter += 1; + } + + if stable_counter >= STABLE_THRESHOLD { + return Ok(median_edge); + } + } + } + } + return Err("failed to find timing error edge"); + } + + #[cfg(feature = "calibrate_wrpll_skew")] + fn calibrate_skew(timer: &mut GlobalTimer) -> Result<(), &'static str> { + info!("calibrating skew to meet timing constraint..."); + + // clear calibrated value + tag_collector::set_tag_offset(0); + let rising = find_edge(true, timer)? as i32; + let falling = find_edge(false, timer)? as i32; + + let width = BEATING_PERIOD - (falling - rising); + let result = falling + width / 2; + tag_collector::set_tag_offset(result as u32); + + info!( + "calibration successful, error zone: {} -> {}, width: {} ({}deg), middle of working region: {}", + rising, + falling, + width, + 360 * width / BEATING_PERIOD, + result, + ); + + Ok(()) + } + pub fn select_recovered_clock(rc: bool, timer: &mut GlobalTimer) { set_isr(false); @@ -539,6 +660,12 @@ pub mod wrpll { // use nFIQ to avoid IRQ being disabled by mutex lock and mess up PLL set_isr(true); info!("WRPLL interrupt enabled"); + + #[cfg(feature = "calibrate_wrpll_skew")] + calibrate_skew(timer).expect("failed to set the correct skew"); + + #[cfg(wrpll_ref_clk = "GT_CDR")] + test_skew(timer).expect("skew test failed"); } } } diff --git a/src/satman/Cargo.toml.tpl b/src/satman/Cargo.toml.tpl index d9cff61..d4fa63c 100644 --- a/src/satman/Cargo.toml.tpl +++ b/src/satman/Cargo.toml.tpl @@ -7,6 +7,7 @@ build = "build.rs" [features] target_zc706 = ["libboard_zynq/target_zc706", "libsupport_zynq/target_zc706", "libconfig/target_zc706", "libboard_artiq/target_zc706"] target_kasli_soc = ["libboard_zynq/target_kasli_soc", "libsupport_zynq/target_kasli_soc", "libconfig/target_kasli_soc", "libboard_artiq/target_kasli_soc"] +calibrate_wrpll_skew = ["libboard_artiq/calibrate_wrpll_skew"] default = ["target_zc706", ] [build-dependencies]