From 2647ef7249f6f511058bb68251f2bfe69cda9310 Mon Sep 17 00:00:00 2001 From: mwojcik Date: Thu, 22 Jul 2021 09:31:53 +0200 Subject: [PATCH] moved si5324, added notes on required porting --- src/libboard_artiqzynq/Cargo.toml | 4 +- src/libboard_artiqzynq/drtio_routing.rs | 5 +- src/libboard_artiqzynq/drtioaux.rs | 1 + src/libboard_artiqzynq/lib.rs | 4 + src/libboard_artiqzynq/proto_core_io.rs | 158 --------------- src/libboard_artiqzynq/si5324.rs | 258 ++++++++++++++++++++++++ src/satman/main.rs | 44 +--- 7 files changed, 270 insertions(+), 204 deletions(-) delete mode 100644 src/libboard_artiqzynq/proto_core_io.rs create mode 100644 src/libboard_artiqzynq/si5324.rs diff --git a/src/libboard_artiqzynq/Cargo.toml b/src/libboard_artiqzynq/Cargo.toml index 0ffd7917..10983b8c 100644 --- a/src/libboard_artiqzynq/Cargo.toml +++ b/src/libboard_artiqzynq/Cargo.toml @@ -7,4 +7,6 @@ authors = ["M-Labs"] name = "board_artiqzync" [dependencies] -io = { path = "../libio", features = ["byteorder"] } \ No newline at end of file +log = "0.4" +io = { path = "../libio", features = ["byteorder"] } +libboard_zynq = { git = "https://git.m-labs.hk/M-Labs/zynq-rs.git"} \ No newline at end of file diff --git a/src/libboard_artiqzynq/drtio_routing.rs b/src/libboard_artiqzynq/drtio_routing.rs index fd65e2fc..b561256e 100644 --- a/src/libboard_artiqzynq/drtio_routing.rs +++ b/src/libboard_artiqzynq/drtio_routing.rs @@ -1,6 +1,7 @@ -use board_misoc::config; // <- port +use board_misoc::config; // <- port; use libconfig? + #[cfg(has_drtio_routing)] -use board_misoc::csr; // <- port +use pl::csr; // <- port use core::fmt; #[cfg(has_drtio_routing)] diff --git a/src/libboard_artiqzynq/drtioaux.rs b/src/libboard_artiqzynq/drtioaux.rs index 52be3652..69a2f805 100644 --- a/src/libboard_artiqzynq/drtioaux.rs +++ b/src/libboard_artiqzynq/drtioaux.rs @@ -107,6 +107,7 @@ pub fn recv(linkno: u8) -> Result, Error> { pub fn recv_timeout(linkno: u8, timeout_ms: Option) -> Result> { let timeout_ms = timeout_ms.unwrap_or(10); + // only place with clock let limit = clock::get_ms() + timeout_ms; while clock::get_ms() < limit { match recv(linkno)? { diff --git a/src/libboard_artiqzynq/lib.rs b/src/libboard_artiqzynq/lib.rs index da96785f..36085192 100644 --- a/src/libboard_artiqzynq/lib.rs +++ b/src/libboard_artiqzynq/lib.rs @@ -1,5 +1,9 @@ pub mod clock; +#[path = "../../../build/pl.rs"] +pub mod pl; + #[cfg(has_drtio)] pub mod drtioaux; pub mod drtio_routing; +pub mod si5324; diff --git a/src/libboard_artiqzynq/proto_core_io.rs b/src/libboard_artiqzynq/proto_core_io.rs deleted file mode 100644 index c165606b..00000000 --- a/src/libboard_artiqzynq/proto_core_io.rs +++ /dev/null @@ -1,158 +0,0 @@ -use core::str::Utf8Error; -use byteorder::{ByteOrder, NativeEndian}; -use alloc::vec; -use alloc::string::String; - -use core_io::{Read, Write, Error as IoError}; - -#[allow(dead_code)] -#[derive(Debug, Clone, PartialEq)] -pub enum ReadStringError { - Utf8(Utf8Error), - Other(T) -} - -pub trait ProtoRead { - type ReadError; - - fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError>; - - #[inline] - fn read_u8(&mut self) -> Result { - let mut bytes = [0; 1]; - self.read_exact(&mut bytes)?; - Ok(bytes[0]) - } - - #[inline] - fn read_u16(&mut self) -> Result { - let mut bytes = [0; 2]; - self.read_exact(&mut bytes)?; - Ok(NativeEndian::read_u16(&bytes)) - } - - #[inline] - fn read_u32(&mut self) -> Result { - let mut bytes = [0; 4]; - self.read_exact(&mut bytes)?; - Ok(NativeEndian::read_u32(&bytes)) - } - - #[inline] - fn read_u64(&mut self) -> Result { - let mut bytes = [0; 8]; - self.read_exact(&mut bytes)?; - Ok(NativeEndian::read_u64(&bytes)) - } - - #[inline] - fn read_bool(&mut self) -> Result { - Ok(self.read_u8()? != 0) - } - - #[inline] - fn read_bytes(&mut self) -> Result<::alloc::vec::Vec, Self::ReadError> { - let length = self.read_u32()?; - let mut value = vec![0; length as usize]; - self.read_exact(&mut value)?; - Ok(value) - } - - #[inline] - fn read_string(&mut self) -> Result<::alloc::string::String, ReadStringError> { - let bytes = self.read_bytes().map_err(ReadStringError::Other)?; - String::from_utf8(bytes).map_err(|err| ReadStringError::Utf8(err.utf8_error())) - } -} - -pub trait ProtoWrite { - type WriteError; - - fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError>; - - #[inline] - fn write_u8(&mut self, value: u8) -> Result<(), Self::WriteError> { - let bytes = [value; 1]; - self.write_all(&bytes) - } - - #[inline] - fn write_i8(&mut self, value: i8) -> Result<(), Self::WriteError> { - let bytes = [value as u8; 1]; - self.write_all(&bytes) - } - - #[inline] - fn write_u16(&mut self, value: u16) -> Result<(), Self::WriteError> { - let mut bytes = [0; 2]; - NativeEndian::write_u16(&mut bytes, value); - self.write_all(&bytes) - } - - #[inline] - fn write_i16(&mut self, value: i16) -> Result<(), Self::WriteError> { - let mut bytes = [0; 2]; - NativeEndian::write_i16(&mut bytes, value); - self.write_all(&bytes) - } - - #[inline] - fn write_u32(&mut self, value: u32) -> Result<(), Self::WriteError> { - let mut bytes = [0; 4]; - NativeEndian::write_u32(&mut bytes, value); - self.write_all(&bytes) - } - - #[inline] - fn write_i32(&mut self, value: i32) -> Result<(), Self::WriteError> { - let mut bytes = [0; 4]; - NativeEndian::write_i32(&mut bytes, value); - self.write_all(&bytes) - } - - #[inline] - fn write_u64(&mut self, value: u64) -> Result<(), Self::WriteError> { - let mut bytes = [0; 8]; - NativeEndian::write_u64(&mut bytes, value); - self.write_all(&bytes) - } - - #[inline] - fn write_i64(&mut self, value: i64) -> Result<(), Self::WriteError> { - let mut bytes = [0; 8]; - NativeEndian::write_i64(&mut bytes, value); - self.write_all(&bytes) - } - - #[inline] - fn write_bool(&mut self, value: bool) -> Result<(), Self::WriteError> { - self.write_u8(value as u8) - } - - #[inline] - fn write_bytes(&mut self, value: &[u8]) -> Result<(), Self::WriteError> { - self.write_u32(value.len() as u32)?; - self.write_all(value) - } - - #[inline] - fn write_string(&mut self, value: &str) -> Result<(), Self::WriteError> { - self.write_bytes(value.as_bytes()) - } -} - -impl ProtoRead for T where T: Read + ?Sized { - type ReadError = IoError; - - fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::ReadError> { - T::read_exact(self, buf) - } -} - -impl ProtoWrite for T where T: Write + ?Sized { - type WriteError = IoError; - - fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::WriteError> { - T::write_all(self, buf) - } -} diff --git a/src/libboard_artiqzynq/si5324.rs b/src/libboard_artiqzynq/si5324.rs new file mode 100644 index 00000000..8327fc0b --- /dev/null +++ b/src/libboard_artiqzynq/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(()) +} diff --git a/src/satman/main.rs b/src/satman/main.rs index 5126d1d9..0882be5c 100644 --- a/src/satman/main.rs +++ b/src/satman/main.rs @@ -7,13 +7,11 @@ extern crate log; use core::convert::TryFrom; use board_misoc::{csr, irq, ident, clock, uart_logger, i2c}; // <- port, use libboard_zynq #[cfg(has_si5324)] -use board_artiq::si5324; // <- move from runtime +use board_artiqzynq::si5324; // <- move from runtime #[cfg(has_wrpll)] use board_artiq::wrpll; // <- port use board_artiq::{spi, drtioaux}; // <- port, use libboard_zynq use board_artiq::drtio_routing; // <- artiqzync -#[cfg(has_hmc830_7043)] -use board_artiq::hmc830_7043; // <- port? mod repeater; #[cfg(has_jdcg)] @@ -296,35 +294,6 @@ fn process_aux_packet(_repeaters: &mut [repeater::Repeater], drtioaux::Packet::JdacBasicRequest { destination: _destination, dacno: _dacno, reqno: _reqno, param: _param } => { forward!(_routing_table, _destination, *_rank, _repeaters, &packet); - #[cfg(has_ad9154)] - let (succeeded, retval) = { - #[cfg(rtio_frequency = "125.0")] - const LINERATE: u64 = 5_000_000_000; - #[cfg(rtio_frequency = "150.0")] - const LINERATE: u64 = 6_000_000_000; - match _reqno { - jdac_common::INIT => (board_artiq::ad9154::setup(_dacno, LINERATE).is_ok(), 0), - jdac_common::PRINT_STATUS => { board_artiq::ad9154::status(_dacno); (true, 0) }, - jdac_common::PRBS => (board_artiq::ad9154::prbs(_dacno).is_ok(), 0), - jdac_common::STPL => (board_artiq::ad9154::stpl(_dacno, 4, 2).is_ok(), 0), - jdac_common::SYSREF_DELAY_DAC => { board_artiq::hmc830_7043::hmc7043::sysref_delay_dac(_dacno, _param); (true, 0) }, - jdac_common::SYSREF_SLIP => { board_artiq::hmc830_7043::hmc7043::sysref_slip(); (true, 0) }, - jdac_common::SYNC => { - match board_artiq::ad9154::sync(_dacno) { - Ok(false) => (true, 0), - Ok(true) => (true, 1), - Err(e) => { - error!("DAC sync failed: {}", e); - (false, 0) - } - } - }, - jdac_common::DDMTD_SYSREF_RAW => (true, jdac_common::measure_ddmdt_phase_raw() as u8), - jdac_common::DDMTD_SYSREF => (true, jdac_common::measure_ddmdt_phase() as u8), - _ => (false, 0) - } - }; - #[cfg(not(has_ad9154))] let (succeeded, retval) = (false, 0); drtioaux::send(0, &drtioaux::Packet::JdacBasicReply { succeeded: succeeded, retval: retval }) @@ -503,17 +472,6 @@ pub extern fn main() -> i32 { wrpll::diagnostics(); init_rtio_crg(); - #[cfg(has_hmc830_7043)] - /* must be the first SPI init because of HMC830 SPI mode selection */ - hmc830_7043::init().expect("cannot initialize HMC830/7043"); - #[cfg(has_ad9154)] - { - jdac_common::init_ddmtd().expect("failed to initialize SYSREF DDMTD core"); - for dacno in 0..csr::CONFIG_AD9154_COUNT { - board_artiq::ad9154::reset_and_detect(dacno as u8).expect("AD9154 DAC not detected"); - } - } - #[cfg(has_drtio_routing)] let mut repeaters = [repeater::Repeater::default(); csr::DRTIOREP.len()]; #[cfg(not(has_drtio_routing))]