diff --git a/artiq/firmware/libboard_artiq/lib.rs b/artiq/firmware/libboard_artiq/lib.rs index b9666732d..0826ceac3 100644 --- a/artiq/firmware/libboard_artiq/lib.rs +++ b/artiq/firmware/libboard_artiq/lib.rs @@ -26,6 +26,8 @@ pub mod rpc_queue; #[cfg(has_si5324)] pub mod si5324; +#[cfg(has_wrpll)] +pub mod wrpll; #[cfg(has_hmc830_7043)] pub mod hmc830_7043; diff --git a/artiq/firmware/libboard_artiq/wrpll.rs b/artiq/firmware/libboard_artiq/wrpll.rs new file mode 100644 index 000000000..dc97d041a --- /dev/null +++ b/artiq/firmware/libboard_artiq/wrpll.rs @@ -0,0 +1,190 @@ +mod i2c { + use board_misoc::{csr, clock}; + + #[derive(Debug, Clone, Copy)] + pub enum Dcxo { + Main, + Helper + } + + fn half_period() { clock::spin_us(100) } + const SDA_MASK: u8 = 2; + const SCL_MASK: u8 = 1; + + fn sda_i(dcxo: Dcxo) -> bool { + let reg = match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_in_read() }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_in_read() }, + }; + reg & SDA_MASK != 0 + } + + fn sda_oe(dcxo: Dcxo, oe: bool) { + let reg = match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() }, + }; + let reg = if oe { reg | SDA_MASK } else { reg & !SDA_MASK }; + match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) } + } + } + + fn sda_o(dcxo: Dcxo, o: bool) { + let reg = match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() }, + }; + let reg = if o { reg | SDA_MASK } else { reg & !SDA_MASK }; + match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) } + } + } + + fn scl_oe(dcxo: Dcxo, oe: bool) { + let reg = match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_read() }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_read() }, + }; + let reg = if oe { reg | SCL_MASK } else { reg & !SCL_MASK }; + match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_oe_write(reg) }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_oe_write(reg) } + } + } + + fn scl_o(dcxo: Dcxo, o: bool) { + let reg = match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_read() }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_read() }, + }; + let reg = if o { reg | SCL_MASK } else { reg & !SCL_MASK }; + match dcxo { + Dcxo::Main => unsafe { csr::wrpll::main_dcxo_gpio_out_write(reg) }, + Dcxo::Helper => unsafe { csr::wrpll::helper_dcxo_gpio_out_write(reg) } + } + } + + pub fn init(dcxo: Dcxo) -> Result<(), &'static str> { + // Set SCL as output, and high level + scl_o(dcxo, true); + scl_oe(dcxo, true); + // Prepare a zero level on SDA so that sda_oe pulls it down + sda_o(dcxo, false); + // Release SDA + sda_oe(dcxo, false); + + // Check the I2C bus is ready + half_period(); + half_period(); + if !sda_i(dcxo) { + // Try toggling SCL a few times + for _bit in 0..8 { + scl_o(dcxo, false); + half_period(); + scl_o(dcxo, true); + half_period(); + } + } + + if !sda_i(dcxo) { + return Err("SDA is stuck low and doesn't get unstuck"); + } + Ok(()) + } + + pub fn start(dcxo: Dcxo) { + // Set SCL high then SDA low + scl_o(dcxo, true); + half_period(); + sda_oe(dcxo, true); + half_period(); + } + + pub fn restart(dcxo: Dcxo) { + // Set SCL low then SDA high */ + scl_o(dcxo, false); + half_period(); + sda_oe(dcxo, false); + half_period(); + // Do a regular start + start(dcxo); + } + + pub fn stop(dcxo: Dcxo) { + // First, make sure SCL is low, so that the target releases the SDA line + scl_o(dcxo, false); + half_period(); + // Set SCL high then SDA high + sda_oe(dcxo, true); + scl_o(dcxo, true); + half_period(); + sda_oe(dcxo, false); + half_period(); + } + + pub fn write(dcxo: Dcxo, data: u8) -> bool { + // MSB first + for bit in (0..8).rev() { + // Set SCL low and set our bit on SDA + scl_o(dcxo, false); + sda_oe(dcxo, data & (1 << bit) == 0); + half_period(); + // Set SCL high ; data is shifted on the rising edge of SCL + scl_o(dcxo, true); + half_period(); + } + // Check ack + // Set SCL low, then release SDA so that the I2C target can respond + scl_o(dcxo, false); + half_period(); + sda_oe(dcxo, false); + // Set SCL high and check for ack + scl_o(dcxo, true); + half_period(); + // returns true if acked (I2C target pulled SDA low) + !sda_i(dcxo) + } + + pub fn read(dcxo: Dcxo, ack: bool) -> u8 { + // Set SCL low first, otherwise setting SDA as input may cause a transition + // on SDA with SCL high which will be interpreted as START/STOP condition. + scl_o(dcxo, false); + half_period(); // make sure SCL has settled low + sda_oe(dcxo, false); + + let mut data: u8 = 0; + + // MSB first + for bit in (0..8).rev() { + scl_o(dcxo, false); + half_period(); + // Set SCL high and shift data + scl_o(dcxo, true); + half_period(); + if sda_i(dcxo) { data |= 1 << bit } + } + // Send ack + // Set SCL low and pull SDA low when acking + scl_o(dcxo, false); + if ack { sda_oe(dcxo, true) } + half_period(); + // then set SCL high + scl_o(dcxo, true); + half_period(); + + data + } +} + +pub fn init() { + info!("initializing..."); + i2c::init(i2c::Dcxo::Main); + i2c::init(i2c::Dcxo::Helper); +} + +pub fn select_recovered_clock(rc: bool) { + info!("select_recovered_clock: {}", rc); +} diff --git a/artiq/firmware/satman/main.rs b/artiq/firmware/satman/main.rs index dc66038c7..fb738f057 100644 --- a/artiq/firmware/satman/main.rs +++ b/artiq/firmware/satman/main.rs @@ -9,7 +9,11 @@ extern crate board_artiq; use core::convert::TryFrom; use board_misoc::{csr, irq, ident, clock, uart_logger, i2c}; -use board_artiq::{spi, si5324, drtioaux}; +#[cfg(has_si5324)] +use board_artiq::si5324; +#[cfg(has_wrpll)] +use board_artiq::wrpll; +use board_artiq::{spi, drtioaux}; use board_artiq::drtio_routing; #[cfg(has_hmc830_7043)] use board_artiq::hmc830_7043; @@ -413,7 +417,7 @@ fn hardware_tick(ts: &mut u64) { } } -#[cfg(rtio_frequency = "150.0")] +#[cfg(all(has_si5324, rtio_frequency = "150.0"))] const SI5324_SETTINGS: si5324::FrequencySettings = si5324::FrequencySettings { n1_hs : 6, @@ -426,7 +430,7 @@ const SI5324_SETTINGS: si5324::FrequencySettings crystal_ref: true }; -#[cfg(rtio_frequency = "125.0")] +#[cfg(all(has_si5324, rtio_frequency = "125.0"))] const SI5324_SETTINGS: si5324::FrequencySettings = si5324::FrequencySettings { n1_hs : 5, @@ -448,8 +452,13 @@ pub extern fn main() -> i32 { info!("software ident {}", csr::CONFIG_IDENTIFIER_STR); info!("gateware ident {}", ident::read(&mut [0; 64])); - i2c::init().expect("I2C initialization failed"); - si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); + #[cfg(has_si5324)] + { + i2c::init().expect("I2C initialization failed"); + si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); + } + #[cfg(has_wrpll)] + wrpll::init(); unsafe { csr::drtio_transceiver::stable_clkin_write(1); } @@ -490,8 +499,13 @@ pub extern fn main() -> i32 { } info!("uplink is up, switching to recovered clock"); - si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks"); - si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); + #[cfg(has_si5324)] + { + si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks"); + si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); + } + #[cfg(has_wrpll)] + wrpll::select_recovered_clock(true); #[cfg(has_jdcg)] { @@ -561,8 +575,11 @@ pub extern fn main() -> i32 { drtiosat_reset_phy(true); drtiosat_reset(true); drtiosat_tsc_loaded(); - info!("uplink is down, switching to local crystal clock"); + info!("uplink is down, switching to local oscillator clock"); + #[cfg(has_si5324)] si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks"); + #[cfg(has_wrpll)] + wrpll::select_recovered_clock(false); } } diff --git a/artiq/gateware/targets/sayma_amc.py b/artiq/gateware/targets/sayma_amc.py index cfd2434f5..dc5d37a6a 100755 --- a/artiq/gateware/targets/sayma_amc.py +++ b/artiq/gateware/targets/sayma_amc.py @@ -130,26 +130,10 @@ class SatelliteBase(MiniSoC): self.add_csr_group("drtiorep", drtiorep_csr_group) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) - self.comb += platform.request("filtered_clk_sel").eq(1) - self.submodules.siphaser = SiPhaser7Series( - si5324_clkin=platform.request("si5324_clkin"), - rx_synchronizer=self.rx_synchronizer, - ultrascale=True, - rtio_clk_freq=rtio_clk_freq) - platform.add_false_path_constraints( - self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) - self.csr_devices.append("siphaser") - self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) - self.csr_devices.append("si5324_rst_n") - i2c = self.platform.request("i2c") - self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) - self.csr_devices.append("i2c") - self.config["I2C_BUS_COUNT"] = 1 - self.config["HAS_SI5324"] = None - if with_wrpll: # TODO: check OE polarity (depends on what was installed on the boards) self.comb += [ + platform.request("filtered_clk_sel").eq(0), platform.request("ddmtd_main_dcxo_oe").eq(1), platform.request("ddmtd_helper_dcxo_oe").eq(1) ] @@ -157,6 +141,23 @@ class SatelliteBase(MiniSoC): main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"), helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c")) self.csr_devices.append("wrpll") + else: + self.comb += platform.request("filtered_clk_sel").eq(1) + self.submodules.siphaser = SiPhaser7Series( + si5324_clkin=platform.request("si5324_clkin"), + rx_synchronizer=self.rx_synchronizer, + ultrascale=True, + rtio_clk_freq=rtio_clk_freq) + platform.add_false_path_constraints( + self.crg.cd_sys.clk, self.siphaser.mmcm_freerun_output) + self.csr_devices.append("siphaser") + self.submodules.si5324_rst_n = gpio.GPIOOut(platform.request("si5324").rst_n) + self.csr_devices.append("si5324_rst_n") + i2c = self.platform.request("i2c") + self.submodules.i2c = gpio.GPIOTristate([i2c.scl, i2c.sda]) + self.csr_devices.append("i2c") + self.config["I2C_BUS_COUNT"] = 1 + self.config["HAS_SI5324"] = None rtio_clk_period = 1e9/rtio_clk_freq gth = self.drtio_transceiver.gths[0]