wrpll: i2c functions, select_recovered_clock placeholder

This commit is contained in:
Sebastien Bourdeauducq 2019-11-27 21:21:00 +08:00
parent 449d2c4f08
commit 4832bfb08c
4 changed files with 235 additions and 25 deletions

View File

@ -26,6 +26,8 @@ pub mod rpc_queue;
#[cfg(has_si5324)] #[cfg(has_si5324)]
pub mod si5324; pub mod si5324;
#[cfg(has_wrpll)]
pub mod wrpll;
#[cfg(has_hmc830_7043)] #[cfg(has_hmc830_7043)]
pub mod hmc830_7043; pub mod hmc830_7043;

View File

@ -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);
}

View File

@ -9,7 +9,11 @@ extern crate board_artiq;
use core::convert::TryFrom; use core::convert::TryFrom;
use board_misoc::{csr, irq, ident, clock, uart_logger, i2c}; 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; use board_artiq::drtio_routing;
#[cfg(has_hmc830_7043)] #[cfg(has_hmc830_7043)]
use board_artiq::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 const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings { = si5324::FrequencySettings {
n1_hs : 6, n1_hs : 6,
@ -426,7 +430,7 @@ const SI5324_SETTINGS: si5324::FrequencySettings
crystal_ref: true crystal_ref: true
}; };
#[cfg(rtio_frequency = "125.0")] #[cfg(all(has_si5324, rtio_frequency = "125.0"))]
const SI5324_SETTINGS: si5324::FrequencySettings const SI5324_SETTINGS: si5324::FrequencySettings
= si5324::FrequencySettings { = si5324::FrequencySettings {
n1_hs : 5, n1_hs : 5,
@ -448,8 +452,13 @@ pub extern fn main() -> i32 {
info!("software ident {}", csr::CONFIG_IDENTIFIER_STR); info!("software ident {}", csr::CONFIG_IDENTIFIER_STR);
info!("gateware ident {}", ident::read(&mut [0; 64])); info!("gateware ident {}", ident::read(&mut [0; 64]));
#[cfg(has_si5324)]
{
i2c::init().expect("I2C initialization failed"); i2c::init().expect("I2C initialization failed");
si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); si5324::setup(&SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324");
}
#[cfg(has_wrpll)]
wrpll::init();
unsafe { unsafe {
csr::drtio_transceiver::stable_clkin_write(1); csr::drtio_transceiver::stable_clkin_write(1);
} }
@ -490,8 +499,13 @@ pub extern fn main() -> i32 {
} }
info!("uplink is up, switching to recovered clock"); info!("uplink is up, switching to recovered clock");
#[cfg(has_si5324)]
{
si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks"); si5324::siphaser::select_recovered_clock(true).expect("failed to switch clocks");
si5324::siphaser::calibrate_skew().expect("failed to calibrate skew"); si5324::siphaser::calibrate_skew().expect("failed to calibrate skew");
}
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(true);
#[cfg(has_jdcg)] #[cfg(has_jdcg)]
{ {
@ -561,8 +575,11 @@ pub extern fn main() -> i32 {
drtiosat_reset_phy(true); drtiosat_reset_phy(true);
drtiosat_reset(true); drtiosat_reset(true);
drtiosat_tsc_loaded(); 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"); si5324::siphaser::select_recovered_clock(false).expect("failed to switch clocks");
#[cfg(has_wrpll)]
wrpll::select_recovered_clock(false);
} }
} }

View File

@ -130,6 +130,18 @@ class SatelliteBase(MiniSoC):
self.add_csr_group("drtiorep", drtiorep_csr_group) self.add_csr_group("drtiorep", drtiorep_csr_group)
self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6) self.config["RTIO_FREQUENCY"] = str(rtio_clk_freq/1e6)
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)
]
self.submodules.wrpll = WRPLL(
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.comb += platform.request("filtered_clk_sel").eq(1)
self.submodules.siphaser = SiPhaser7Series( self.submodules.siphaser = SiPhaser7Series(
si5324_clkin=platform.request("si5324_clkin"), si5324_clkin=platform.request("si5324_clkin"),
@ -147,17 +159,6 @@ class SatelliteBase(MiniSoC):
self.config["I2C_BUS_COUNT"] = 1 self.config["I2C_BUS_COUNT"] = 1
self.config["HAS_SI5324"] = None self.config["HAS_SI5324"] = None
if with_wrpll:
# TODO: check OE polarity (depends on what was installed on the boards)
self.comb += [
platform.request("ddmtd_main_dcxo_oe").eq(1),
platform.request("ddmtd_helper_dcxo_oe").eq(1)
]
self.submodules.wrpll = WRPLL(
main_dcxo_i2c=platform.request("ddmtd_main_dcxo_i2c"),
helper_dxco_i2c=platform.request("ddmtd_helper_dcxo_i2c"))
self.csr_devices.append("wrpll")
rtio_clk_period = 1e9/rtio_clk_freq rtio_clk_period = 1e9/rtio_clk_freq
gth = self.drtio_transceiver.gths[0] gth = self.drtio_transceiver.gths[0]
platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2) platform.add_period_constraint(gth.txoutclk, rtio_clk_period/2)