diff --git a/artiq/firmware/libboard_artiq/ad9154.rs b/artiq/firmware/libboard_artiq/ad9154.rs index 5b876b371..ff5f08311 100644 --- a/artiq/firmware/libboard_artiq/ad9154.rs +++ b/artiq/firmware/libboard_artiq/ad9154.rs @@ -184,7 +184,7 @@ fn dac_setup(dacno: u8, linerate: u64) -> Result<(), &'static str> { write(ad9154_reg::SPI_PAGEINDX, 0x3); // A and B dual - write(ad9154_reg::INTERP_MODE, 0x01); // 2x + write(ad9154_reg::INTERP_MODE, 0x03); // 4x write(ad9154_reg::MIX_MODE, 0); write(ad9154_reg::DATA_FORMAT, 0*ad9154_reg::BINARY_FORMAT); // s16 write(ad9154_reg::DATAPATH_CTRL, diff --git a/artiq/firmware/libboard_artiq/hmc830_7043.rs b/artiq/firmware/libboard_artiq/hmc830_7043.rs index 9d62a774f..6e6212808 100644 --- a/artiq/firmware/libboard_artiq/hmc830_7043.rs +++ b/artiq/firmware/libboard_artiq/hmc830_7043.rs @@ -117,6 +117,9 @@ mod hmc830 { // Max reference frequency: 350MHz, however f_ref >= 200MHz requires // setting 0x08[21]=1 // + // Warning: Output divider is not synchronized! Set to 1 for deterministic + // phase at the output. + // // :param r_div: reference divider [1, 16383] // :param n_div: VCO divider, integer part. Integer-N mode: [16, 2**19-1] // fractional mode: [20, 2**19-4] @@ -155,10 +158,11 @@ mod hmc830 { pub mod hmc7043 { use board_misoc::{csr, clock}; - // All frequencies assume 1.2GHz HMC830 output - pub const DAC_CLK_DIV: u16 = 1; // 1200MHz - pub const FPGA_CLK_DIV: u16 = 8; // 150MHz - pub const SYSREF_DIV: u16 = 128; // 9.375MHz + // Warning: dividers are not synchronized with HMC830 clock input! + // Set DAC_CLK_DIV to 1 for deterministic phase. + pub const DAC_CLK_DIV: u16 = 1; // 2400MHz + pub const FPGA_CLK_DIV: u16 = 16; // 150MHz + pub const SYSREF_DIV: u16 = 256; // 9.375MHz const HMC_SYSREF_DIV: u16 = SYSREF_DIV*8; // 1.171875MHz (must be <= 4MHz) // enabled, divider, output config @@ -385,35 +389,18 @@ pub mod hmc7043 { } } - pub fn sysref_offset_dac(dacno: u8, phase_offset: u16) { - /* Analog delay resolution: 25ps - * Digital delay resolution: 1/2 input clock cycle = 416ps for 1.2GHz - * 16*25ps = 400ps: limit analog delay to 16 steps instead of 32. - */ - let analog_delay = (phase_offset % 17) as u8; - let digital_delay = (phase_offset / 17) as u8; + pub fn sysref_offset_dac(dacno: u8, phase_offset: u8) { spi_setup(); if dacno == 0 { - write(0x00d5, analog_delay); - write(0x00d6, digital_delay); + write(0x00d5, phase_offset); } else if dacno == 1 { - write(0x00e9, analog_delay); - write(0x00ea, digital_delay); + write(0x00e9, phase_offset); } else { unimplemented!(); } clock::spin_us(100); } - pub fn sysref_offset_fpga(phase_offset: u16) { - let analog_delay = (phase_offset % 17) as u8; - let digital_delay = (phase_offset / 17) as u8; - spi_setup(); - write(0x0111, analog_delay); - write(0x0112, digital_delay); - clock::spin_us(100); - } - pub fn sysref_slip() { spi_setup(); write(0x0002, 0x02); @@ -429,11 +416,11 @@ pub fn init() -> Result<(), &'static str> { hmc830::detect()?; hmc830::init(); - // 1.2GHz out + // 2.4GHz out #[cfg(hmc830_ref = "100")] - hmc830::set_dividers(1, 24, 0, 2); + hmc830::set_dividers(1, 24, 0, 1); #[cfg(hmc830_ref = "150")] - hmc830::set_dividers(2, 32, 0, 2); + hmc830::set_dividers(2, 32, 0, 1); hmc830::check_locked()?; diff --git a/artiq/firmware/libboard_artiq/jesd204sync.rs b/artiq/firmware/libboard_artiq/jesd204sync.rs index 8d66f81bc..eb51590e1 100644 --- a/artiq/firmware/libboard_artiq/jesd204sync.rs +++ b/artiq/firmware/libboard_artiq/jesd204sync.rs @@ -3,167 +3,7 @@ use board_misoc::{csr, config}; use hmc830_7043::hmc7043; use ad9154; -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 { - 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_slip_to(target: bool) -> Result { - let mut slips = 0; - while sysref_sample() != target { - hmc7043::sysref_slip(); - slips += 1; - if slips > 1024 { - return Err("failed to reach SYSREF transition"); - } - } - Ok(slips) -} - -fn sysref_rtio_check_period(phase_offset: u16) -> Result<(), &'static str> { - const N: usize = 32; - let mut nslips = [0; N]; - let mut error = false; - - // 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 - sysref_rtio_slip_to(false)?; - for i in 0..N { - nslips[i] = sysref_rtio_slip_to(i % 2 == 0)?; - if nslips[i] != hmc7043::SYSREF_DIV/2 { - error = true; - } - } - if error { - info!(" SYSREF slip half-periods: {:?}", nslips); - return Err("unexpected SYSREF slip half-periods seen"); - } else { - info!(" SYSREF slip half-periods at FPGA have expected length ({})", hmc7043::SYSREF_DIV/2); - } - Ok(()) -} - -fn sysref_rtio_align(phase_offset: u16) -> Result<(), &'static str> { - // This needs to take place 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..."); - - sysref_rtio_check_period(phase_offset)?; - - // 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 - let slips0 = sysref_rtio_slip_to(false)?; - // get to the edge of the 0->1 transition (our final setpoint) - let slips1 = sysref_rtio_slip_to(true)?; - info!(" ...done ({}/{} slips)", slips0, slips1); - - 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() -> 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) -} - -fn sysref_cal_dac(dacno: u8) -> Result { +fn sysref_cal_dac(dacno: u8) -> Result { info!("calibrating SYSREF phase at DAC-{}...", dacno); let mut d = 0; @@ -182,12 +22,12 @@ fn sysref_cal_dac(dacno: u8) -> Result { } d += 1; - if d > 128 { + if d > 23 { return Err("no sync errors found when scanning delay"); } } - d += 17; // get away from jitter + d += 5; // get away from jitter hmc7043::sysref_offset_dac(dacno, d); ad9154::dac_sync(dacno)?; @@ -200,7 +40,7 @@ fn sysref_cal_dac(dacno: u8) -> Result { } d += 1; - if d > 128 { + if d > 23 { return Err("no sync errors found when scanning delay"); } } @@ -210,7 +50,7 @@ fn sysref_cal_dac(dacno: u8) -> Result { Ok(phase) } -fn sysref_dac_align(dacno: u8, phase: u16) -> Result<(), &'static str> { +fn sysref_dac_align(dacno: u8, phase: u8) -> Result<(), &'static str> { let mut margin_minus = None; let mut margin_plus = None; @@ -218,7 +58,7 @@ fn sysref_dac_align(dacno: u8, phase: u16) -> Result<(), &'static str> { hmc7043::sysref_offset_dac(dacno, phase); ad9154::dac_sync(dacno)?; - for d in 0..128 { + for d in 0..24 { hmc7043::sysref_offset_dac(dacno, phase - d); let realign_occured = ad9154::dac_sync(dacno)?; if realign_occured { @@ -229,7 +69,7 @@ fn sysref_dac_align(dacno: u8, phase: u16) -> Result<(), &'static str> { hmc7043::sysref_offset_dac(dacno, phase); ad9154::dac_sync(dacno)?; - for d in 0..128 { + for d in 0..24 { hmc7043::sysref_offset_dac(dacno, phase + d); let realign_occured = ad9154::dac_sync(dacno)?; if realign_occured { @@ -242,7 +82,7 @@ fn sysref_dac_align(dacno: u8, phase: u16) -> Result<(), &'static str> { let margin_minus = margin_minus.unwrap(); let margin_plus = margin_plus.unwrap(); info!(" margins: -{} +{}", margin_minus, margin_plus); - if margin_minus < 10 || margin_plus < 10 { + if margin_minus < 5 || margin_plus < 5 { return Err("SYSREF margins at DAC are too small, board needs recalibration"); } } else { diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index c905d5df4..11866e389 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -115,9 +115,12 @@ fn startup() { { board_artiq::ad9154::jesd_reset(false); board_artiq::ad9154::init(); + /* + TODO: if let Err(e) = board_artiq::jesd204sync::sysref_auto_rtio_align() { error!("failed to align SYSREF at FPGA: {}", e); } + */ if let Err(e) = board_artiq::jesd204sync::sysref_auto_dac_align() { error!("failed to align SYSREF at DAC: {}", e); } diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 180ccbcba..2f226f4ab 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -489,9 +489,12 @@ pub extern fn main() -> i32 { info!("TSC loaded from uplink"); #[cfg(has_ad9154)] { + /* + TODO: if let Err(e) = board_artiq::jesd204sync::sysref_auto_rtio_align() { error!("failed to align SYSREF at FPGA: {}", e); } + */ if let Err(e) = board_artiq::jesd204sync::sysref_auto_dac_align() { error!("failed to align SYSREF at DAC: {}", e); } diff --git a/artiq/gateware/jesd204_tools.py b/artiq/gateware/jesd204_tools.py index 5e8c5e85d..1aa605a8d 100644 --- a/artiq/gateware/jesd204_tools.py +++ b/artiq/gateware/jesd204_tools.py @@ -91,22 +91,3 @@ class UltrascaleTX(Module, AutoCSR): self.submodules.control = JESD204BCoreTXControl(self.core) self.core.register_jsync(platform.request("dac_sync", dac)) self.core.register_jref(jesd_crg.jref) - - -# This assumes: -# * coarse RTIO frequency = 16*SYSREF frequency -# * JESD and coarse RTIO clocks are the same -# (only reset may differ). -# * SYSREF meets setup/hold at the FPGA when sampled -# in the JESD/RTIO domain. -# -# Look at the 4 LSBs of the coarse RTIO timestamp counter -# to determine SYSREF phase. - -class SysrefSampler(Module, AutoCSR): - def __init__(self, coarse_ts, jref): - self.sample_result = CSRStatus() - - sample = Signal() - self.sync.jesd += If(coarse_ts[:4] == 0, sample.eq(jref)) - self.specials += MultiReg(sample, self.sample_result.status) diff --git a/artiq/gateware/targets/sayma_amc.py b/artiq/gateware/targets/sayma_amc.py index bba5d3711..2970877c5 100755 --- a/artiq/gateware/targets/sayma_amc.py +++ b/artiq/gateware/targets/sayma_amc.py @@ -223,10 +223,6 @@ class Standalone(MiniSoC, AMPSoC, RTMCommon): self.get_native_sdram_if()) self.csr_devices.append("rtio_analyzer") - self.submodules.sysref_sampler = jesd204_tools.SysrefSampler( - self.rtio_tsc.coarse_ts, self.ad9154_crg.jref) - self.csr_devices.append("sysref_sampler") - class MasterDAC(MiniSoC, AMPSoC, RTMCommon): """ @@ -395,10 +391,6 @@ class MasterDAC(MiniSoC, AMPSoC, RTMCommon): self.submodules.routing_table = rtio.RoutingTableAccess(self.cri_con) self.csr_devices.append("routing_table") - self.submodules.sysref_sampler = jesd204_tools.SysrefSampler( - self.rtio_tsc.coarse_ts, self.ad9154_crg.jref) - self.csr_devices.append("sysref_sampler") - def workaround_us_lvds_tristate(platform): # Those shoddy Kintex Ultrascale FPGAs take almost a microsecond to change the direction of a @@ -684,10 +676,6 @@ class Satellite(BaseSoC, RTMCommon): self.config["I2C_BUS_COUNT"] = 1 self.config["HAS_SI5324"] = None - self.submodules.sysref_sampler = jesd204_tools.SysrefSampler( - self.rtio_tsc.coarse_ts, self.ad9154_crg.jref) - self.csr_devices.append("sysref_sampler") - rtio_clk_period = 1e9/rtio_clk_freq gth = self.drtio_transceiver.gths[0] platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)