From 0ae2138034cd4e66528c3c3e6aeba08f51c998b9 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sat, 29 May 2021 16:15:27 +0800 Subject: [PATCH] kasli-soc: preliminary si5324 support --- src/runtime/src/i2c.rs | 145 +++++++++------------ src/runtime/src/main.rs | 19 +++ src/runtime/src/si5324.rs | 258 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+), 89 deletions(-) create mode 100644 src/runtime/src/si5324.rs diff --git a/src/runtime/src/i2c.rs b/src/runtime/src/i2c.rs index 5c06d154..b8c5bcf4 100644 --- a/src/runtime/src/i2c.rs +++ b/src/runtime/src/i2c.rs @@ -1,100 +1,67 @@ -#[cfg(feature = "target_zc706")] -mod i2c { - use libboard_zynq; - use crate::artiq_raise; +use libboard_zynq; +use crate::artiq_raise; - static mut I2C_BUS: Option = None; +pub static mut I2C_BUS: Option = None; - pub extern fn start(busno: i32) { - if busno > 0 { - artiq_raise!("I2CError", "I2C bus could not be accessed"); - } - unsafe { - if (&mut I2C_BUS).as_mut().unwrap().start().is_err() { - artiq_raise!("I2CError", "I2C start failed"); - } - } +pub extern fn start(busno: i32) { + if busno > 0 { + artiq_raise!("I2CError", "I2C bus could not be accessed"); } - - pub extern fn restart(busno: i32) { - if busno > 0 { - artiq_raise!("I2CError", "I2C bus could not be accessed"); + unsafe { + if (&mut I2C_BUS).as_mut().unwrap().start().is_err() { + artiq_raise!("I2CError", "I2C start failed"); } - unsafe { - if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() { - artiq_raise!("I2CError", "I2C restart failed"); - } - } - } - - pub extern fn stop(busno: i32) { - if busno > 0 { - artiq_raise!("I2CError", "I2C bus could not be accessed"); - } - unsafe { - if (&mut I2C_BUS).as_mut().unwrap().stop().is_err() { - artiq_raise!("I2CError", "I2C stop failed"); - } - } - } - - pub extern fn write(busno: i32, data: i32) -> bool { - if busno > 0 { - artiq_raise!("I2CError", "I2C bus could not be accessed"); - } - unsafe { - match (&mut I2C_BUS).as_mut().unwrap().write(data as u8) { - Ok(r) => r, - Err(_) => artiq_raise!("I2CError", "I2C write failed"), - } - } - } - - pub extern fn read(busno: i32, ack: bool) -> i32 { - if busno > 0 { - artiq_raise!("I2CError", "I2C bus could not be accessed"); - } - unsafe { - match (&mut I2C_BUS).as_mut().unwrap().read(ack) { - Ok(r) => r as i32, - Err(_) => artiq_raise!("I2CError", "I2C read failed"), - } - } - } - - pub fn init() { - let mut i2c = libboard_zynq::i2c::I2c::i2c0(); - i2c.init().expect("I2C bus initialization failed"); - unsafe { I2C_BUS = Some(i2c) }; } } -#[cfg(not(feature = "target_zc706"))] -mod i2c { - use crate::artiq_raise; - - pub extern fn start(_busno: i32) { - artiq_raise!("I2CError", "No I2C bus"); +pub extern fn restart(busno: i32) { + if busno > 0 { + artiq_raise!("I2CError", "I2C bus could not be accessed"); } - - pub extern fn restart(_busno: i32) { - artiq_raise!("I2CError", "No I2C bus"); - } - - pub extern fn stop(_busno: i32) { - artiq_raise!("I2CError", "No I2C bus"); - } - - pub extern fn write(_busno: i32, _data: i32) -> bool { - artiq_raise!("I2CError", "No I2C bus"); - } - - pub extern fn read(_busno: i32, _ack: bool) -> i32 { - artiq_raise!("I2CError", "No I2C bus"); - } - - pub fn init() { + unsafe { + if (&mut I2C_BUS).as_mut().unwrap().restart().is_err() { + artiq_raise!("I2CError", "I2C restart failed"); + } } } -pub use i2c::*; +pub extern fn stop(busno: i32) { + if busno > 0 { + artiq_raise!("I2CError", "I2C bus could not be accessed"); + } + unsafe { + if (&mut I2C_BUS).as_mut().unwrap().stop().is_err() { + artiq_raise!("I2CError", "I2C stop failed"); + } + } +} + +pub extern fn write(busno: i32, data: i32) -> bool { + if busno > 0 { + artiq_raise!("I2CError", "I2C bus could not be accessed"); + } + unsafe { + match (&mut I2C_BUS).as_mut().unwrap().write(data as u8) { + Ok(r) => r, + Err(_) => artiq_raise!("I2CError", "I2C write failed"), + } + } +} + +pub extern fn read(busno: i32, ack: bool) -> i32 { + if busno > 0 { + artiq_raise!("I2CError", "I2C bus could not be accessed"); + } + unsafe { + match (&mut I2C_BUS).as_mut().unwrap().read(ack) { + Ok(r) => r as i32, + Err(_) => artiq_raise!("I2CError", "I2C read failed"), + } + } +} + +pub fn init() { + let mut i2c = libboard_zynq::i2c::I2c::i2c0(); + i2c.init().expect("I2C bus initialization failed"); + unsafe { I2C_BUS = Some(i2c) }; +} diff --git a/src/runtime/src/main.rs b/src/runtime/src/main.rs index c27bba7d..57a2503c 100644 --- a/src/runtime/src/main.rs +++ b/src/runtime/src/main.rs @@ -45,6 +45,8 @@ mod mgmt; mod analyzer; mod irq; mod i2c; +#[cfg(feature = "target_kasli_soc")] +mod si5324; fn init_gateware() { // Set up PS->PL clocks @@ -160,6 +162,20 @@ async fn report_async_rtio_errors() { } } +#[cfg(feature = "target_kasli_soc")] +// 125MHz output, from crystal, 7 Hz +const SI5324_SETTINGS: si5324::FrequencySettings + = si5324::FrequencySettings { + n1_hs : 10, + nc1_ls : 4, + n2_hs : 10, + n2_ls : 19972, + n31 : 4565, + n32 : 4565, + bwsel : 4, + crystal_ref: true +}; + static mut LOG_BUFFER: [u8; 1<<17] = [0; 1<<17]; #[no_mangle] @@ -183,6 +199,9 @@ pub fn main_core0() { info!("detected gateware: {}", identifier_read(&mut [0; 64])); i2c::init(); + #[cfg(feature = "target_kasli_soc")] + si5324::setup(unsafe { (&mut i2c::I2C_BUS).as_mut().unwrap() }, + &SI5324_SETTINGS, si5324::Input::Ckin1).expect("cannot initialize Si5324"); let cfg = match Config::new() { Ok(cfg) => cfg, diff --git a/src/runtime/src/si5324.rs b/src/runtime/src/si5324.rs new file mode 100644 index 00000000..8327fc0b --- /dev/null +++ b/src/runtime/src/si5324.rs @@ -0,0 +1,258 @@ +use core::result; +use log::info; +use libboard_zynq::i2c::I2c; + +type Result = result::Result; + +const ADDRESS: u8 = 0x68; + +// NOTE: the logical parameters DO NOT MAP to physical values written +// into registers. They have to be mapped; see the datasheet. +// DSPLLsim reports the logical parameters in the design summary, not +// the physical register values. +pub struct FrequencySettings { + pub n1_hs: u8, + pub nc1_ls: u32, + pub n2_hs: u8, + pub n2_ls: u32, + pub n31: u32, + pub n32: u32, + pub bwsel: u8, + 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") + } + if settings.nc1_ls > (1 << 20) { + return Err("NC1_LS is too high") + } + if (settings.n2_ls % 2) == 1 { + return Err("N2_LS must be even") + } + if settings.n2_ls > (1 << 20) { + return Err("N2_LS is too high") + } + if settings.n31 > (1 << 19) { + return Err("N31 is too high") + } + if settings.n32 > (1 << 19) { + return Err("N32 is too high") + } + let r = FrequencySettings { + n1_hs: match settings.n1_hs { + 4 => 0b000, + 5 => 0b001, + 6 => 0b010, + 7 => 0b011, + 8 => 0b100, + 9 => 0b101, + 10 => 0b110, + 11 => 0b111, + _ => return Err("N1_HS has an invalid value") + }, + nc1_ls: settings.nc1_ls - 1, + n2_hs: match settings.n2_hs { + 4 => 0b000, + 5 => 0b001, + 6 => 0b010, + 7 => 0b011, + 8 => 0b100, + 9 => 0b101, + 10 => 0b110, + 11 => 0b111, + _ => return Err("N2_HS has an invalid value") + }, + n2_ls: settings.n2_ls - 1, + n31: settings.n31 - 1, + n32: settings.n32 - 1, + bwsel: settings.bwsel, + crystal_ref: settings.crystal_ref + }; + Ok(r) +} + +fn write(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> { + i2c.start().unwrap(); + if !i2c.write(ADDRESS << 1).unwrap() { + return Err("Si5324 failed to ack write address") + } + if !i2c.write(reg).unwrap() { + return Err("Si5324 failed to ack register") + } + if !i2c.write(val).unwrap() { + return Err("Si5324 failed to ack value") + } + i2c.stop().unwrap(); + Ok(()) +} + +fn write_no_ack_value(i2c: &mut I2c, reg: u8, val: u8) -> Result<()> { + i2c.start().unwrap(); + if !i2c.write(ADDRESS << 1).unwrap() { + return Err("Si5324 failed to ack write address") + } + if !i2c.write(reg).unwrap() { + return Err("Si5324 failed to ack register") + } + i2c.write(val).unwrap(); + i2c.stop().unwrap(); + Ok(()) +} + +fn read(i2c: &mut I2c, reg: u8) -> Result { + i2c.start().unwrap(); + if !i2c.write(ADDRESS << 1).unwrap() { + return Err("Si5324 failed to ack write address") + } + if !i2c.write(reg).unwrap() { + return Err("Si5324 failed to ack register") + } + i2c.restart().unwrap(); + if !i2c.write((ADDRESS << 1) | 1).unwrap() { + return Err("Si5324 failed to ack read address") + } + let val = i2c.read(false).unwrap(); + i2c.stop().unwrap(); + Ok(val) +} + +fn rmw(i2c: &mut I2c, reg: u8, f: F) -> Result<()> where + F: Fn(u8) -> u8 { + let value = read(i2c, reg)?; + write(i2c, reg, f(value))?; + Ok(()) +} + +fn ident(i2c: &mut I2c) -> Result { + Ok(((read(i2c, 134)? as u16) << 8) | (read(i2c, 135)? as u16)) +} + +fn soft_reset(i2c: &mut I2c) -> Result<()> { + //TODO write_no_ack_value(i2c, 136, read(136)? | 0x80)?; + //TODO clock::spin_us(10_000); + Ok(()) +} + +fn has_xtal(i2c: &mut I2c) -> Result { + Ok((read(i2c, 129)? & 0x01) == 0) // LOSX_INT=0 +} + +fn has_ckin(i2c: &mut I2c, input: Input) -> Result { + match input { + Input::Ckin1 => Ok((read(i2c, 129)? & 0x02) == 0), // LOS1_INT=0 + Input::Ckin2 => Ok((read(i2c, 129)? & 0x04) == 0), // LOS2_INT=0 + } +} + +fn locked(i2c: &mut I2c) -> Result { + Ok((read(i2c, 130)? & 0x01) == 0) // LOL_INT=0 +} + +fn monitor_lock(i2c: &mut I2c) -> Result<()> { + info!("waiting for Si5324 lock..."); + // TODO let t = clock::get_ms(); + while !locked(i2c)? { + // Yes, lock can be really slow. + /*if clock::get_ms() > t + 20000 { + return Err("Si5324 lock timeout"); + }*/ + } + info!(" ...locked"); + Ok(()) +} + +fn init(i2c: &mut I2c) -> Result<()> { + info!("init test"); + #[cfg(feature = "target_kasli_soc")] + { + i2c.pca9548_select(0x70, 0)?; + i2c.pca9548_select(0x71, 1 << 3)?; + } + + if ident(i2c)? != 0x0182 { + return Err("Si5324 does not have expected product number"); + } + + soft_reset(i2c)?; + Ok(()) +} + +pub fn bypass(i2c: &mut I2c, input: Input) -> Result<()> { + let cksel_reg = match input { + Input::Ckin1 => 0b00, + Input::Ckin2 => 0b01, + }; + init(i2c)?; + rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0 + rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?; // CKSEL_REG + rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00 + rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111 + rmw(i2c, 0, |v| (v & 0xfd) | 0x02)?; // BYPASS_REG=1 + Ok(()) +} + +pub fn setup(i2c: &mut I2c, settings: &FrequencySettings, input: Input) -> Result<()> { + let s = map_frequency_settings(settings)?; + let cksel_reg = match input { + Input::Ckin1 => 0b00, + Input::Ckin2 => 0b01, + }; + + init(i2c)?; + if settings.crystal_ref { + rmw(i2c, 0, |v| v | 0x40)?; // FREE_RUN=1 + } + rmw(i2c, 2, |v| (v & 0x0f) | (s.bwsel << 4))?; + rmw(i2c, 21, |v| v & 0xfe)?; // CKSEL_PIN=0 + rmw(i2c, 3, |v| (v & 0x2f) | (cksel_reg << 6) | 0x10)?; // CKSEL_REG, SQ_ICAL=1 + rmw(i2c, 4, |v| (v & 0x3f) | (0b00 << 6))?; // AUTOSEL_REG=b00 + rmw(i2c, 6, |v| (v & 0xc0) | 0b111111)?; // SFOUT2_REG=b111 SFOUT1_REG=b111 + write(i2c, 25, (s.n1_hs << 5 ) as u8)?; + write(i2c, 31, (s.nc1_ls >> 16) as u8)?; + write(i2c, 32, (s.nc1_ls >> 8 ) as u8)?; + write(i2c, 33, (s.nc1_ls) as u8)?; + write(i2c, 34, (s.nc1_ls >> 16) as u8)?; // write to NC2_LS as well + write(i2c, 35, (s.nc1_ls >> 8 ) as u8)?; + write(i2c, 36, (s.nc1_ls) as u8)?; + write(i2c, 40, (s.n2_hs << 5 ) as u8 | (s.n2_ls >> 16) as u8)?; + write(i2c, 41, (s.n2_ls >> 8 ) as u8)?; + write(i2c, 42, (s.n2_ls) as u8)?; + write(i2c, 43, (s.n31 >> 16) as u8)?; + write(i2c, 44, (s.n31 >> 8) as u8)?; + write(i2c, 45, (s.n31) as u8)?; + write(i2c, 46, (s.n32 >> 16) as u8)?; + write(i2c, 47, (s.n32 >> 8) as u8)?; + write(i2c, 48, (s.n32) as u8)?; + rmw(i2c, 137, |v| v | 0x01)?; // FASTLOCK=1 + rmw(i2c, 136, |v| v | 0x40)?; // ICAL=1 + + if !has_xtal(i2c)? { + return Err("Si5324 misses XA/XB signal"); + } + if !has_ckin(i2c, input)? { + return Err("Si5324 misses clock input signal"); + } + + monitor_lock(i2c)?; + Ok(()) +} + +pub fn select_input(i2c: &mut I2c, input: Input) -> Result<()> { + let cksel_reg = match input { + Input::Ckin1 => 0b00, + Input::Ckin2 => 0b01, + }; + rmw(i2c, 3, |v| (v & 0x3f) | (cksel_reg << 6))?; + if !has_ckin(i2c, input)? { + return Err("Si5324 misses clock input signal"); + } + monitor_lock(i2c)?; + Ok(()) +}