diff --git a/src/gateware/kasli_soc.py b/src/gateware/kasli_soc.py index ba2f0ca..a575bb9 100755 --- a/src/gateware/kasli_soc.py +++ b/src/gateware/kasli_soc.py @@ -224,8 +224,6 @@ class GenericMaster(SoCCore): self.config["HW_REV"] = description["hw_rev"] - self.submodules += SMAClkinForward(self.platform) - data_pads = [platform.request("sfp", i) for i in range(4)] self.submodules.gt_drtio = gtx_7series.GTX( @@ -259,8 +257,18 @@ class GenericMaster(SoCCore): self.comb += ext_async_rst.eq(self.sys_crg.clk_sw_fsm.o_clk_sw & ~gtx0.tx_init.done) self.specials += MultiReg(self.sys_crg.clk_sw_fsm.o_clk_sw & self.sys_crg.mmcm_locked, self.gt_drtio.clk_path_ready, odomain="bootstrap") - self.config["HAS_SI5324"] = None - self.config["SI5324_SOFT_RESET"] = None + if with_wrpll: + self.submodules.sma_pll = wrpll.SMA_PLL(platform.request("sma_clkin")) + self.csr_devices.append("sma_pll") + + self.submodules.main_dcxo = si549.Si549(platform.request("ddmtd_main_dcxo_i2c")) + self.csr_devices.append("main_dcxo") + self.config["HAS_SI549"] = None + self.config["WRPLL_REF_CLK"] = "SMA_CLKIN" + else: + self.submodules += SMAClkinForward(self.platform) + self.config["HAS_SI5324"] = None + self.config["SI5324_SOFT_RESET"] = None self.rtio_channels = [] has_grabber = any(peripheral["type"] == "grabber" for peripheral in description["peripherals"]) @@ -565,6 +573,7 @@ class GenericSatellite(SoCCore): self.csr_devices.append("wrpll") self.comb += self.ps7.core.core0.nfiq.eq(self.wrpll.ev.irq) self.config["HAS_SI549"] = None + self.config["WRPLL_REF_CLK"] = "GTX_CDR" else: self.submodules.siphaser = SiPhaser7Series( si5324_clkin=platform.request("cdr_clk"), diff --git a/src/gateware/wrpll.py b/src/gateware/wrpll.py index 995c91f..e6f9ceb 100644 --- a/src/gateware/wrpll.py +++ b/src/gateware/wrpll.py @@ -123,4 +123,65 @@ class WRPLL(Module, AutoCSR): self.main_tag_ev.stb.trigger.eq(main_tag_stb_sys) ] - self.submodules.ev = SharedIRQ(self.ref_tag_ev, self.main_tag_ev) \ No newline at end of file + self.submodules.ev = SharedIRQ(self.ref_tag_ev, self.main_tag_ev) + + +class SMA_PLL(Module, AutoCSR): + def __init__(self, sma_clkin): + freq = 125e9 + period = 1e9/freq # ns + + mmcm_locked = Signal() + mmcm_fb_clk = Signal() + ref_clk = Signal() + self.clock_domains.cd_ref = ClockDomain() + + self.mmcm_locked = CSRStatus() + self.mmcm_reset = CSRStorage() #TODO for i_RST + + self.drp_addr = CSRStorage(7) + self.drp_in = CSRStorage(16) + self.drp_w_en = CSRStorage() + self.drp_en = CSRStorage() + self.drp_clk = CSRStorage() + self.drp_out = CSRStatus(16) + self.drp_ready = CSRStatus() + + # # # + + self.specials += [ + # MMCME2 is capable to accept 10Mhz input while PLLE2 only support down to 19Mhz input (DS191) + Instance("MMCME2_ADV", + p_BANDWIDTH="LOW", # lower jitter + o_LOCKED=self.mmcm_locked.status, + + p_CLKIN1_PERIOD=period, + i_CLKIN1=ClockSignal("sys"), + i_RST=ResetSignal("sys"), + i_CLKINSEL=1, # 1=CLKIN1 0=CLKIN2 + + # VCO @ 1Ghz + p_CLKFBOUT_MULT_F=8.0, + # p_CLKFBOUT_MULT = 1.0, # M = p_CLKFBOUT_MULT_F/p_CLKFBOUT_MULT + p_DIVCLK_DIVIDE=1, + i_CLKFBIN=mmcm_fb_clk, o_CLKFBOUT=mmcm_fb_clk, + + # 62.5MHz + p_CLKOUT0_DIVIDE_F=16, p_CLKOUT0_PHASE=0.0, o_CLKOUT0=ref_clk, + + # Dynamic Reconfiguration Port + i_DADDR = self.drp_addr.storage, + i_DI = self.drp_in.storage, + i_DWE = self.drp_w_en.storage, + i_DEN = self.drp_en.storage, + i_DCLK = self.drp_clk.storage, + o_DO = self.drp_out.status, + o_DRDY = self.drp_ready.status + ), + Instance("BUFG", i_I=ref_clk, o_O=self.cd_ref.clk), + AsyncResetSynchronizer(self.cd_ref, ~self.mmcm_locked.status), + + # debug output + Instance("OBUFDS", i_I=self.cd_ref.clk, o_O=sma_clkin.p, o_OB=sma_clkin.n) + ] + diff --git a/src/libboard_artiq/src/si549.rs b/src/libboard_artiq/src/si549.rs index f98c738..cb77118 100644 --- a/src/libboard_artiq/src/si549.rs +++ b/src/libboard_artiq/src/si549.rs @@ -547,3 +547,64 @@ pub mod wrpll { } } } + +#[cfg(has_sma_pll)] +pub mod sma_pll { + use super::*; + + // Based on "DRP State Machine" section from XAPP888 + mod mmcm { + + use super::*; + + fn one_clock_cycle(timer: &mut GlobalTimer) { + unsafe { + csr::sma_pll::drp_clk_write(1); + timer.delay_us(1); + csr::sma_pll::drp_clk_write(0); + timer.delay_us(1); + } + } + + fn set_addr(address: u8) { + unsafe { + csr::sma_pll::drp_addr_write(address); + } + } + + fn set_enable(en: bool) { + unsafe { + let val = if en { 1 } else { 0 }; + csr::sma_pll::drp_en_write(val); + } + } + + fn get_data() -> u16 { + unsafe { csr::sma_pll::drp_out_read() } + } + + fn drp_ready() -> bool { + unsafe { csr::sma_pll::drp_ready_read() == 1 } + } + + pub fn read(timer: &mut GlobalTimer, address: u8) -> u16 { + set_addr(address); + set_enable(true); + // Set DADDR on the MMCM and assert DEN for one clock cycle + one_clock_cycle(timer); + set_enable(false); + while !drp_ready() { + // keep the clock signal until data is ready + one_clock_cycle(timer); + } + get_data() + } + } + + pub fn setup(timer: &mut GlobalTimer) { + for addr in 7..12 { + info!("address = {:#x} | val = {:016b}", addr, mmcm::read(timer, addr)); + } + info!("running SMA PLL"); + } +} diff --git a/src/runtime/src/main.rs b/src/runtime/src/main.rs index 50ffb03..ea060e9 100644 --- a/src/runtime/src/main.rs +++ b/src/runtime/src/main.rs @@ -104,6 +104,11 @@ pub fn main_core0() { io_expander1 .init(i2c_bus) .expect("I2C I/O expander #1 initialization failed"); + + // Drive CLK_SEL to true + #[cfg(has_si549)] + io_expander0.set(1, 7, true); + // Drive TX_DISABLE to false on SFP0..3 io_expander0.set(0, 1, false); io_expander1.set(0, 1, false); diff --git a/src/runtime/src/rtio_clocking.rs b/src/runtime/src/rtio_clocking.rs index e6cf632..dbbe6fc 100644 --- a/src/runtime/src/rtio_clocking.rs +++ b/src/runtime/src/rtio_clocking.rs @@ -4,6 +4,8 @@ use ksupport::i2c; use libboard_artiq::pl; #[cfg(has_si5324)] use libboard_artiq::si5324; +#[cfg(has_si549)] +use libboard_artiq::si549; #[cfg(has_si5324)] use libboard_zynq::i2c::I2c; use libboard_zynq::timer::GlobalTimer; @@ -260,6 +262,48 @@ fn setup_si5324(i2c: &mut I2c, timer: &mut GlobalTimer, clk: RtioClock) { si5324::setup(i2c, &si5324_settings, si5324_ref_input, timer).expect("cannot initialize Si5324"); } +#[cfg(has_si549)] +fn setup_si549(timer: &mut GlobalTimer, clk: RtioClock) { + let si549_settings = match clk { + RtioClock::Int_100 => { + info!("using internal 100MHz RTIO clock"); + si549::FrequencySetting { + main: si549::DividerConfig { + hsdiv: 0x06C, + lsdiv: 0, + fbdiv: 0x046C5F49797, + }, + } + } + RtioClock::Int_125 => { + info!("using internal 125MHz RTIO clock"); + si549::FrequencySetting { + main: si549::DividerConfig { + hsdiv: 0x058, + lsdiv: 0, + fbdiv: 0x04815791F25, + }, + } + } + _ => { + // same setting as Int_125, but fallback to default + warn!( + "rtio_clock setting '{:?}' is unsupported. Falling back to default 125MHz RTIO clock.", + clk + ); + si549::FrequencySetting { + main: si549::DividerConfig { + hsdiv: 0x058, + lsdiv: 0, + fbdiv: 0x04815791F25, + }, + } + } + }; + + si549::main_setup(timer, si549_settings).expect("cannot initialize main Si549"); +} + pub fn init(timer: &mut GlobalTimer, cfg: &Config) { let clk = get_rtio_clock_cfg(cfg); #[cfg(has_si5324)] @@ -273,6 +317,11 @@ pub fn init(timer: &mut GlobalTimer, cfg: &Config) { _ => setup_si5324(i2c, timer, clk), } } + #[cfg(has_si549)] + { + si549::sma_pll::setup(timer); + setup_si549(timer, clk); + } #[cfg(has_drtio)] init_drtio(timer);