diff --git a/artiq/firmware/libboard_artiq/hmc830_7043.rs b/artiq/firmware/libboard_artiq/hmc830_7043.rs index d5f418a5e..adf753141 100644 --- a/artiq/firmware/libboard_artiq/hmc830_7043.rs +++ b/artiq/firmware/libboard_artiq/hmc830_7043.rs @@ -154,15 +154,15 @@ pub mod hmc7043 { (true, DAC_CLK_DIV, 0x08, false), // 2: DAC0_CLK (true, SYSREF_DIV, 0x01, true), // 3: DAC0_SYSREF (true, SYSREF_DIV, 0x10, true), // 4: AMC_FPGA_SYSREF0 - (true, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1 + (false, FPGA_CLK_DIV, 0x10, true), // 5: AMC_FPGA_SYSREF1 (false, 0, 0x10, false), // 6: unused - (true, SYSREF_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0 + (true, FPGA_CLK_DIV, 0x10, true), // 7: RTM_FPGA_SYSREF0 (true, FPGA_CLK_DIV, 0x08, false), // 8: GTP_CLK0_IN (false, 0, 0x10, false), // 9: unused (false, 0, 0x10, false), // 10: unused (false, 0, 0x08, false), // 11: unused / uFL (false, 0, 0x10, false), // 12: unused - (false, SYSREF_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1 + (false, FPGA_CLK_DIV, 0x10, true), // 13: RTM_FPGA_SYSREF1 ]; fn spi_setup() { diff --git a/artiq/firmware/satman/jdac_common.rs b/artiq/firmware/satman/jdac_common.rs new file mode 100644 index 000000000..3c185516f --- /dev/null +++ b/artiq/firmware/satman/jdac_common.rs @@ -0,0 +1,74 @@ +pub const INIT: u8 = 0x00; +pub const PRINT_STATUS: u8 = 0x01; +pub const PRBS: u8 = 0x02; +pub const STPL: u8 = 0x03; + +pub const SYSREF_DELAY_DAC: u8 = 0x10; +pub const SYSREF_SLIP: u8 = 0x11; +pub const SYNC: u8 = 0x12; + +pub const DDMTD_SYSREF_RAW: u8 = 0x20; +pub const DDMTD_SYSREF: u8 = 0x21; + + +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; +} + +pub 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) + } +} + +pub const RAW_DDMTD_N_SHIFT: i32 = 6; +pub const RAW_DDMTD_N: i32 = 1 << RAW_DDMTD_N_SHIFT; +pub const DDMTD_DITHER_BITS: i32 = 1; +pub const DDMTD_N_SHIFT: i32 = RAW_DDMTD_N_SHIFT + DDMTD_DITHER_BITS; +pub const DDMTD_N: i32 = 1 << DDMTD_N_SHIFT; + +#[cfg(has_ad9154)] +use board_misoc::{clock, csr}; + +#[cfg(has_ad9154)] +pub 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") + } + } +} + +#[cfg(has_ad9154)] +pub fn measure_ddmdt_phase_raw() -> i32 { + unsafe { csr::sysref_ddmtd::dt_read() as i32 } +} + +#[cfg(has_ad9154)] +pub 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 +} diff --git a/artiq/firmware/satman/jdac_requests.rs b/artiq/firmware/satman/jdac_requests.rs deleted file mode 100644 index 0df8335bd..000000000 --- a/artiq/firmware/satman/jdac_requests.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub const INIT: u8 = 0x00; -pub const PRINT_STATUS: u8 = 0x01; -pub const PRBS: u8 = 0x02; -pub const STPL: u8 = 0x03; - -pub const SYSREF_DELAY_DAC: u8 = 0x10; -pub const SYSREF_SLIP: u8 = 0x11; -pub const SYNC: u8 = 0x12; diff --git a/artiq/firmware/satman/jdcg.rs b/artiq/firmware/satman/jdcg.rs index ed214f4f4..4f8736fe7 100644 --- a/artiq/firmware/satman/jdcg.rs +++ b/artiq/firmware/satman/jdcg.rs @@ -51,7 +51,7 @@ pub mod jdac { use board_artiq::drtioaux; use super::jesd; - use super::super::jdac_requests; + use super::super::jdac_common; pub fn basic_request(dacno: u8, reqno: u8, param: u8) -> Result { if let Err(e) = drtioaux::send(1, &drtioaux::Packet::JdacBasicRequest { @@ -95,24 +95,24 @@ pub mod jdac { return Err("JESD core PHY not done"); } - basic_request(dacno, jdac_requests::INIT, 0)?; + basic_request(dacno, jdac_common::INIT, 0)?; // JESD ready depends on JSYNC being valid, so DAC init needs to happen first if !jesd::ready(dacno) { error!("JESD core reported not ready, sending DAC status print request"); - basic_request(dacno, jdac_requests::PRINT_STATUS, 0)?; + basic_request(dacno, jdac_common::PRINT_STATUS, 0)?; return Err("JESD core reported not ready"); } jesd::prbs(dacno, true); - basic_request(dacno, jdac_requests::PRBS, 0)?; + basic_request(dacno, jdac_common::PRBS, 0)?; jesd::prbs(dacno, false); jesd::stpl(dacno, true); - basic_request(dacno, jdac_requests::STPL, 0)?; + basic_request(dacno, jdac_common::STPL, 0)?; jesd::stpl(dacno, false); - basic_request(dacno, jdac_requests::INIT, 0)?; + basic_request(dacno, jdac_common::INIT, 0)?; clock::spin_us(5000); if !jesd::jsync(dacno) { @@ -130,7 +130,7 @@ pub mod jesd204sync { use board_misoc::{csr, clock, config}; use super::jdac; - use super::super::jdac_requests; + use super::super::jdac_common; const HMC7043_ANALOG_DELAY_RANGE: u8 = 24; @@ -138,7 +138,7 @@ pub mod jesd204sync { 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) { + match jdac::basic_request(dacno, jdac_common::SYSREF_DELAY_DAC, phase_offset) { Ok(_) => Ok(()), Err(e) => Err(e) } @@ -146,90 +146,43 @@ pub mod jesd204sync { fn hmc7043_sysref_slip() -> Result<(), &'static str> { - match jdac::basic_request(0, jdac_requests::SYSREF_SLIP, 0) { + match jdac::basic_request(0, jdac_common::SYSREF_SLIP, 0) { Ok(_) => Ok(()), Err(e) => Err(e) } } fn ad9154_sync(dacno: u8) -> Result { - match jdac::basic_request(dacno, jdac_requests::SYNC, 0) { + match jdac::basic_request(dacno, jdac_common::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 measure_ddmdt_phase_raw() -> Result { + Ok(jdac::basic_request(0, jdac_common::DDMTD_SYSREF_RAW, 0)? as i32) } - 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 measure_ddmdt_phase() -> Result { + Ok(jdac::basic_request(0, jdac_common::DDMTD_SYSREF, 0)? as i32) } 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 modulo = if raw { jdac_common::RAW_DDMTD_N } else { jdac_common::DDMTD_N }; let measurement = if raw { measure_ddmdt_phase_raw } else { measure_ddmdt_phase }; - let ntests = if raw { 15000 } else { 150 }; + let ntests = if raw { 150 } else { 15 }; 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 quadrant = measure_ddmdt_phase()?; let center_offset = - if quadrant < DDMTD_N/4 || quadrant > 3*DDMTD_N/4 { + if quadrant < jdac_common::DDMTD_N/4 || quadrant > 3*jdac_common::DDMTD_N/4 { modulo/2 } else { 0 @@ -238,7 +191,7 @@ pub mod jesd204sync { let mut min = modulo; let mut max = 0; for _ in 0..ntests { - let m = (measurement() + center_offset) % modulo; + let m = (measurement()? + center_offset) % modulo; if m < min { min = m; } @@ -268,11 +221,11 @@ pub mod jesd204sync { let tolerance = 1; info!("testing HMC7043 SYSREF slip against DDMTD..."); - let mut old_phase = measure_ddmdt_phase(); + 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; + let phase = measure_ddmdt_phase()?; + let step = (jdac_common::DDMTD_N + old_phase - phase) % jdac_common::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"); @@ -295,7 +248,7 @@ pub mod jesd204sync { 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); + const SYSREF_SH_MOD: i32 = 1 << (jdac_common::DDMTD_N_SHIFT + SYSREF_SH_PRECISION_SHIFT); #[derive(Default)] struct SysrefShLimits { @@ -318,7 +271,7 @@ pub mod jesd204sync { } let current = sysref_sh_error(); - let phase = measure_ddmdt_phase(); + 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; @@ -335,7 +288,7 @@ pub mod jesd204sync { 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; + let deviation = (phase - average + jdac_common::DDMTD_N) % jdac_common::DDMTD_N; if deviation > ret { ret = deviation; } @@ -345,7 +298,7 @@ pub mod jesd204sync { fn reach_sysref_ddmtd_target(target: i32, tolerance: i32) -> Result { for _ in 0..1024 { - let delta = (measure_ddmdt_phase() - target + DDMTD_N) % DDMTD_N; + let delta = (measure_ddmdt_phase()? - target + jdac_common::DDMTD_N) % jdac_common::DDMTD_N; if delta <= tolerance { return Ok(delta) } @@ -360,11 +313,11 @@ pub mod jesd204sync { if rising_average < falling_average { (rising_average + falling_average)/2 } else { - ((falling_average - (DDMTD_N - rising_average))/2 + DDMTD_N) % DDMTD_N + ((falling_average - (jdac_common::DDMTD_N - rising_average))/2 + jdac_common::DDMTD_N) % jdac_common::DDMTD_N }; info!(" SYSREF calibration coarse target: {}", coarse_target); reach_sysref_ddmtd_target(coarse_target, 8)?; - let target = measure_ddmdt_phase(); + let target = measure_ddmdt_phase()?; info!(" ...done, target={}", target); Ok(target) } @@ -447,15 +400,14 @@ pub mod jesd204sync { } 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_average = jdac_common::average_phases(&sysref_sh_limits.rising_phases, SYSREF_SH_MOD); + let falling_average = jdac_common::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); diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 3b5096fd8..12d80c2d0 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -22,7 +22,7 @@ mod repeater; #[cfg(has_jdcg)] mod jdcg; #[cfg(any(has_ad9154, has_jdcg))] -pub mod jdac_requests; +pub mod jdac_common; fn drtiosat_reset(reset: bool) { unsafe { @@ -306,13 +306,13 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater], #[cfg(rtio_frequency = "150.0")] const LINERATE: u64 = 6_000_000_000; match _reqno { - jdac_requests::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0), - jdac_requests::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) }, - jdac_requests::PRBS => (board_artiq::ad9154::prbs(_dacno).is_ok(), 0), - jdac_requests::STPL => (board_artiq::ad9154::stpl(_dacno, 4, 2).is_ok(), 0), - 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 => { + jdac_common::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0), + jdac_common::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) }, + jdac_common::PRBS => (board_artiq::ad9154::prbs(_dacno).is_ok(), 0), + jdac_common::STPL => (board_artiq::ad9154::stpl(_dacno, 4, 2).is_ok(), 0), + jdac_common::SYSREF_DELAY_DAC => { board_artiq::hmc830_7043::hmc7043::sysref_delay_dac(_dacno, _param); (true, 0) }, + jdac_common::SYSREF_SLIP => { board_artiq::hmc830_7043::hmc7043::sysref_slip(); (true, 0) }, + jdac_common::SYNC => { match board_artiq::ad9154::sync(_dacno) { Ok(false) => (true, 0), Ok(true) => (true, 1), @@ -321,7 +321,9 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater], (false, 0) } } - } + }, + jdac_common::DDMTD_SYSREF_RAW => (true, jdac_common::measure_ddmdt_phase_raw() as u8), + jdac_common::DDMTD_SYSREF => (true, jdac_common::measure_ddmdt_phase() as u8), _ => (false, 0) } }; @@ -472,6 +474,7 @@ pub extern fn main() -> i32 { hmc830_7043::init().expect("cannot initialize HMC830/7043"); #[cfg(has_ad9154)] { + jdac_common::init_ddmtd().expect("failed to initialize SYSREF DDMTD core"); for dacno in 0..csr::CONFIG_AD9154_COUNT { board_artiq::ad9154::reset_and_detect(dacno as u8).expect("AD9154 DAC not detected"); } diff --git a/artiq/gateware/targets/sayma_amc.py b/artiq/gateware/targets/sayma_amc.py index 18e6f70e0..a676c9219 100755 --- a/artiq/gateware/targets/sayma_amc.py +++ b/artiq/gateware/targets/sayma_amc.py @@ -311,12 +311,6 @@ class Satellite(SatelliteBase): self.jdcg_0.jesd.core.register_jref(self.sysref_sampler.jref) self.jdcg_1.jesd.core.register_jref(self.sysref_sampler.jref) - # DDMTD - # https://github.com/sinara-hw/Sayma_RTM/issues/68 - sysref_pads = platform.request("amc_fpga_sysref", 1) - self.submodules.sysref_ddmtd = jesd204_tools.DDMTD(sysref_pads, self.rtio_clk_freq) - self.csr_devices.append("sysref_ddmtd") - class SimpleSatellite(SatelliteBase): def __init__(self, **kwargs): diff --git a/artiq/gateware/targets/sayma_rtm.py b/artiq/gateware/targets/sayma_rtm.py index 9864563d1..777495aa6 100755 --- a/artiq/gateware/targets/sayma_rtm.py +++ b/artiq/gateware/targets/sayma_rtm.py @@ -16,6 +16,7 @@ from misoc.targets.sayma_rtm import BaseSoC, soc_sayma_rtm_args, soc_sayma_rtm_a from misoc.integration.builder import Builder, builder_args, builder_argdict from artiq.gateware import rtio +from artiq.gateware import jesd204_tools from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series from artiq.gateware.drtio.transceiver import gtp_7series from artiq.gateware.drtio.siphaser import SiPhaser7Series @@ -257,6 +258,15 @@ class Satellite(_SatelliteBase): platform.request("hmc7043_out_en")) self.csr_devices.append("hmc7043_out_en") + # DDMTD + sysref_pads = platform.request("rtm_fpga_sysref", 0) + self.submodules.sysref_ddmtd = jesd204_tools.DDMTD(sysref_pads, self.rtio_clk_freq) + self.csr_devices.append("sysref_ddmtd") + platform.add_false_path_constraints( + self.sysref_ddmtd.cd_helper.clk, self.drtio_transceiver.gtps[0].txoutclk) + platform.add_false_path_constraints( + self.sysref_ddmtd.cd_helper.clk, self.crg.cd_sys.clk) + class SatmanSoCBuilder(Builder): def __init__(self, *args, **kwargs):