From c34d00cbc97123954713a90061b2b180f7478b0f Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Wed, 7 Mar 2018 00:06:39 +0800 Subject: [PATCH] drtio: implement Si5324 phaser gateware and partial firmware support --- artiq/firmware/libboard_artiq/si5324.rs | 56 ++++++++++++---- artiq/firmware/runtime/main.rs | 2 +- artiq/firmware/satman/main.rs | 21 +++--- artiq/gateware/drtio/si_phaser.py | 85 +++++++++++++++++++++++++ artiq/gateware/targets/kasli.py | 12 ++-- 5 files changed, 147 insertions(+), 29 deletions(-) create mode 100644 artiq/gateware/drtio/si_phaser.py diff --git a/artiq/firmware/libboard_artiq/si5324.rs b/artiq/firmware/libboard_artiq/si5324.rs index db96c7acc..9aeeb36be 100644 --- a/artiq/firmware/libboard_artiq/si5324.rs +++ b/artiq/firmware/libboard_artiq/si5324.rs @@ -1,6 +1,6 @@ use core::result; use board::clock; -#[cfg(not(si5324_soft_reset))] +#[cfg(any(not(si5324_soft_reset), has_si_phaser))] use board::csr; use i2c; @@ -47,6 +47,11 @@ pub struct FrequencySettings { pub crystal_ref: bool } +pub enum Input { + Ckin1, + Ckin2, +} + fn map_frequency_settings(settings: &FrequencySettings) -> Result { if settings.nc1_ls != 0 && (settings.nc1_ls % 2) == 1 { return Err("NC1_LS must be 0 or even") @@ -160,8 +165,11 @@ fn has_xtal() -> Result { Ok((read(129)? & 0x01) == 0) // LOSX_INT=0 } -fn has_clkin2() -> Result { - Ok((read(129)? & 0x04) == 0) // LOS2_INT=0 +fn has_ckin(input: Input) -> Result { + match input { + Input::Ckin1 => Ok((read(129)? & 0x02) == 0), // LOS1_INT=0 + Input::Ckin2 => Ok((read(129)? & 0x04) == 0), // LOS2_INT=0 + } } fn locked() -> Result { @@ -180,7 +188,7 @@ fn monitor_lock() -> Result<()> { Ok(()) } -pub fn setup(settings: &FrequencySettings) -> Result<()> { +pub fn setup(settings: &FrequencySettings, input: Input) -> Result<()> { let s = map_frequency_settings(settings)?; #[cfg(not(si5324_soft_reset))] @@ -203,12 +211,16 @@ pub fn setup(settings: &FrequencySettings) -> Result<()> { #[cfg(si5324_soft_reset)] soft_reset()?; + let cksel_reg = match input { + Input::Ckin1 => 0b00, + Input::Ckin2 => 0b01, + }; if settings.crystal_ref { write(0, read(0)? | 0x40)?; // FREE_RUN=1 } write(2, (read(2)? & 0x0f) | (s.bwsel << 4))?; write(21, read(21)? & 0xfe)?; // CKSEL_PIN=0 - write(3, (read(3)? & 0x3f) | (0b01 << 6) | 0x10)?; // CKSEL_REG=b01 SQ_ICAL=1 + write(3, (read(3)? & 0x3f) | (cksel_reg << 6) | 0x10)?; // CKSEL_REG, SQ_ICAL=1 write(4, (read(4)? & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00 write(6, (read(6)? & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111 write(25, (s.n1_hs << 5 ) as u8)?; @@ -233,20 +245,40 @@ pub fn setup(settings: &FrequencySettings) -> Result<()> { if !has_xtal()? { return Err("Si5324 misses XA/XB signal"); } - if !has_clkin2()? { - return Err("Si5324 misses CLKIN2 signal"); + if !has_ckin(input)? { + return Err("Si5324 misses clock input signal"); } monitor_lock()?; Ok(()) } -pub fn select_ext_input(external: bool) -> Result<()> { - if external { - write(3, (read(3)? & 0x3f) | (0b00 << 6))?; // CKSEL_REG=b00 - } else { - write(3, (read(3)? & 0x3f) | (0b01 << 6))?; // CKSEL_REG=b01 +pub fn select_input(input: Input) -> Result<()> { + let cksel_reg = match input { + Input::Ckin1 => 0b00, + Input::Ckin2 => 0b01, + }; + write(3, (read(3)? & 0x3f) | (cksel_reg << 6))?; + if !has_ckin(input)? { + return Err("Si5324 misses clock input signal"); } monitor_lock()?; Ok(()) } + +#[cfg(has_si_phaser)] +pub fn select_recovered_clock(rc: bool) -> Result<()> { + write(3, (read(3)? & 0xdf) | (1 << 5))?; // DHOLD=1 + unsafe { + csr::si_phaser::switch_clocks_write(if rc { 1 } else { 0 }); + } + write(3, (read(3)? & 0xdf) | (1 << 5))?; // DHOLD=0 + monitor_lock()?; + Ok(()) +} + +#[cfg(has_si_phaser)] +pub fn calibrate_skew() -> Result<()> { + // TODO: implement + Ok(()) +} diff --git a/artiq/firmware/runtime/main.rs b/artiq/firmware/runtime/main.rs index b2756ff31..181981df6 100644 --- a/artiq/firmware/runtime/main.rs +++ b/artiq/firmware/runtime/main.rs @@ -152,7 +152,7 @@ fn setup_si5324_as_synthesizer() bwsel : 3, crystal_ref: true }; - board_artiq::si5324::setup(&SI5324_SETTINGS).expect("cannot initialize Si5324"); + board_artiq::si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin2).expect("cannot initialize Si5324"); } #[cfg(has_ethmac)] diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index 7e069b752..e1b60179d 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -195,14 +195,14 @@ fn process_errors() { #[cfg(rtio_frequency = "150.0")] const SI5324_SETTINGS: si5324::FrequencySettings - = si5324::FrequencySettings { - n1_hs : 9, - nc1_ls : 4, + = si5324::FrequencySettings { + n1_hs : 6, + nc1_ls : 6, n2_hs : 10, - n2_ls : 33732, - n31 : 9370, - n32 : 7139, - bwsel : 3, + n2_ls : 270, + n31 : 75, + n32 : 75, + bwsel : 4, crystal_ref: true }; @@ -225,7 +225,7 @@ fn startup() { /* must be the first SPI init because of HMC830 SPI mode selection */ hmc830_7043::init().expect("cannot initialize HMC830/7043"); i2c::init(); - si5324::setup(&SI5324_SETTINGS).expect("cannot initialize Si5324"); + si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); unsafe { csr::drtio_transceiver::stable_clkin_write(1); } @@ -235,7 +235,8 @@ fn startup() { process_errors(); } info!("link is up, switching to recovered clock"); - si5324::select_ext_input(true).expect("failed to switch clocks"); + si5324::select_recovered_clock(true).expect("failed to switch clocks"); + si5324::calibrate_skew().expect("failed to calibrate skew"); drtio_reset(false); drtio_reset_phy(false); while drtio_link_rx_up() { @@ -245,7 +246,7 @@ fn startup() { drtio_reset_phy(true); drtio_reset(true); info!("link is down, switching to local crystal clock"); - si5324::select_ext_input(false).expect("failed to switch clocks"); + si5324::select_recovered_clock(false).expect("failed to switch clocks"); } } diff --git a/artiq/gateware/drtio/si_phaser.py b/artiq/gateware/drtio/si_phaser.py new file mode 100644 index 000000000..292dd3a9f --- /dev/null +++ b/artiq/gateware/drtio/si_phaser.py @@ -0,0 +1,85 @@ +from migen import * +from migen.genlib.cdc import MultiReg + +from misoc.interconnect.csr import * + + +# This code assumes 125MHz system clock and 150MHz RTIO frequency. + +class SiPhaser7Series(Module, AutoCSR): + def __init__(self, si5324_clkin, si5324_clkout_fabric): + self.switch_clocks = CSRStorage() + self.phase_shift = CSR() + self.phase_shift_done = CSRStatus() + self.sample_result = CSRStatus() + + # 125MHz system clock to 150MHz. VCO @ 625MHz. + # Used to provide a startup clock to the transceiver through the Si, + # we do not use the crystal reference so that the PFD (f3) frequency + # can be high. + mmcm_freerun_fb = Signal() + mmcm_freerun_output = Signal() + self.specials += \ + Instance("MMCME2_BASE", + p_CLKIN1_PERIOD=1e9/125e6, + i_CLKIN1=ClockSignal("sys"), + i_RST=ResetSignal("sys"), + + p_CLKFBOUT_MULT_F=6.0, p_DIVCLK_DIVIDE=1, + + o_CLKFBOUT=mmcm_freerun_fb, i_CLKFBIN=mmcm_freerun_fb, + + p_CLKOUT0_DIVIDE_F=5.0, o_CLKOUT0=mmcm_freerun_output, + ) + + # 150MHz to 150MHz with controllable phase shift, VCO @ 1200MHz. + # Inserted between CDR and output to Si, used to correct + # non-determinstic skew of Si5324. + mmcm_ps_fb = Signal() + mmcm_ps_output = Signal() + self.specials += \ + Instance("MMCME2_ADV", + p_CLKIN1_PERIOD=1e9/150e6, + i_CLKIN1=ClockSignal("rtio_rx0"), + i_RST=ResetSignal("rtio_rx0"), + i_CLKINSEL=1, # yes, 1=CLKIN1 0=CLKIN2 + + p_CLKFBOUT_MULT_F=8.0, + p_CLKOUT0_DIVIDE_F=8.0, + p_DIVCLK_DIVIDE=1, + + o_CLKFBOUT=mmcm_ps_fb, i_CLKFBIN=mmcm_ps_fb, + + p_CLKOUT0_USE_FINE_PS="TRUE", + o_CLKOUT0=mmcm_ps_output, + + i_PSCLK=ClockSignal(), + i_PSEN=self.phase_shift.re, + i_PSINCDEC=self.phase_shift.r, + o_PSDONE=self.phase_shift_done.status, + ) + + si5324_clkin_se = Signal() + self.specials += [ + Instance("BUFGMUX", + i_I0=mmcm_freerun_output, + i_I1=mmcm_ps_output, + i_S=self.switch_clocks.storage, + o_O=si5324_clkin_se + ), + Instance("OBUFDS", + i_I=si5324_clkin_se, + o_O=si5324_clkin.p, o_OB=si5324_clkin.n + ) + ] + + si5324_clkout_se = Signal() + self.specials += \ + Instance("IBUFDS", + p_DIFF_TERM="TRUE", p_IBUF_LOW_PWR="TRUE", + i_I=si5324_clkout_fabric.p, i_IB=si5324_clkout_fabric.n, + o_O=si5324_clkout_se), + + clkout_sample1 = Signal() # IOB register + self.sync.rtio_rx0 += clkout_sample1.eq(si5324_clkout_se) + self.specials += MultiReg(clkout_sample1, self.sample_result.status) diff --git a/artiq/gateware/targets/kasli.py b/artiq/gateware/targets/kasli.py index 9a3b31a74..77791cfed 100755 --- a/artiq/gateware/targets/kasli.py +++ b/artiq/gateware/targets/kasli.py @@ -21,6 +21,7 @@ from artiq.gateware.amp import AMPSoC from artiq.gateware import rtio from artiq.gateware.rtio.phy import ttl_simple, ttl_serdes_7series, spi2 from artiq.gateware.drtio.transceiver import gtp_7series +from artiq.gateware.drtio.si_phaser import SiPhaser7Series from artiq.gateware.drtio.rx_synchronizer import XilinxRXSynchronizer from artiq.gateware.drtio import DRTIOMaster, DRTIOSatellite from artiq.build_soc import build_artiq_soc @@ -573,12 +574,11 @@ class Satellite(BaseSoC): self.add_memory_group("drtio_aux", ["drtio0_aux"]) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) - si5324_clkin = platform.request("si5324_clkin") - self.specials += \ - Instance("OBUFDS", - i_I=ClockSignal("rtio_rx0"), - o_O=si5324_clkin.p, o_OB=si5324_clkin.n - ) + self.submodules.si_phaser = SiPhaser7Series( + si5324_clkin=platform.request("si5324_clkin"), + si5324_clkout_fabric=platform.request("si5324_clkout_fabric") + ) + self.csr_devices.append("si_phaser") i2c = self.platform.request("i2c") self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) self.csr_devices.append("i2c")