diff --git a/src/attenuator.rs b/src/attenuator.rs index 9350bc0..ca9eaba 100644 --- a/src/attenuator.rs +++ b/src/attenuator.rs @@ -4,123 +4,123 @@ use core::assert; use crate::urukul::Error; pub struct Attenuator { - spi: SPI, - data: [u8; 4], + spi: SPI, + data: [u8; 4], } impl Attenuator where - SPI: Transfer + SPI: Transfer { - pub fn new(spi: SPI) -> Self { - Attenuator { - spi, - // data[y] refers to the yth byte for SPI communication - data: [0, 0, 0, 0], - } - } + pub fn new(spi: SPI) -> Self { + Attenuator { + spi, + // data[y] refers to the yth byte for SPI communication + data: [0, 0, 0, 0], + } + } - /* - * Set attenuations of all attenuators - * att[x] refers to the attenuation for channel x - */ - pub fn set_attenuation(&mut self, att: [f32; 4]) -> Result<(), Error> { - for i in 0..4 { - let mut atten = att[i]; - if att[i] > 31.5 { - atten = 31.5; - } - if att[i] < 0.0 { - atten = 0.0; - } - // Set data as attenuation * 2 - // Flip data using bitwise XOR, active low data - // Data is most signifant attenuator first - self.data[3-i] = (((atten * 2.0) as u8) ^ 0xFF) << 2 - } - let mut clone = self.data.clone(); - // Transmit SPI once to set attenuation - self.spi.transfer(&mut clone) - .map(|_| ()) - .map_err(|_| Error::AttenuatorError) - } + /* + * Set attenuations of all attenuators + * att[x] refers to the attenuation for channel x + */ + pub fn set_attenuation(&mut self, att: [f32; 4]) -> Result<(), Error> { + for i in 0..4 { + let mut atten = att[i]; + if att[i] > 31.5 { + atten = 31.5; + } + if att[i] < 0.0 { + atten = 0.0; + } + // Set data as attenuation * 2 + // Flip data using bitwise XOR, active low data + // Data is most signifant attenuator first + self.data[3-i] = (((atten * 2.0) as u8) ^ 0xFF) << 2 + } + let mut clone = self.data.clone(); + // Transmit SPI once to set attenuation + self.spi.transfer(&mut clone) + .map(|_| ()) + .map_err(|_| Error::AttenuatorError) + } - pub fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Error> { - assert!(channel < 4); - let mut arr: [f32; 4] = self.get_attenuation()?; - arr[channel as usize] = attenuation; - self.set_attenuation(arr).map(|_| ()) - } + pub fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Error> { + assert!(channel < 4); + let mut arr: [f32; 4] = self.get_attenuation()?; + arr[channel as usize] = attenuation; + self.set_attenuation(arr).map(|_| ()) + } - pub fn get_channel_attenuation(&mut self, channel: u8) -> Result> { - assert!(channel < 4); - match self.get_attenuation() { - Ok(arr) => Ok(arr[channel as usize]), - Err(e) => Err(e), - } - } + pub fn get_channel_attenuation(&mut self, channel: u8) -> Result> { + assert!(channel < 4); + match self.get_attenuation() { + Ok(arr) => Ok(arr[channel as usize]), + Err(e) => Err(e), + } + } - pub fn get_attenuation(&mut self) -> Result<[f32; 4], Error> { - let mut clone = self.data.clone(); - match self.spi.transfer(&mut clone).map_err(Error::SPI) { - Ok(arr) => { - let mut ret :[f32; 4] = [0.0; 4]; - for index in 0..4 { - ret[index] = ((arr[3 - index] ^ 0xFC) as f32) / 8.0; - } - Ok(ret) - }, - Err(e) => Err(e), - } - } + pub fn get_attenuation(&mut self) -> Result<[f32; 4], Error> { + let mut clone = self.data.clone(); + match self.spi.transfer(&mut clone).map_err(Error::SPI) { + Ok(arr) => { + let mut ret :[f32; 4] = [0.0; 4]; + for index in 0..4 { + ret[index] = ((arr[3 - index] ^ 0xFC) as f32) / 8.0; + } + Ok(ret) + }, + Err(e) => Err(e), + } + } - /* - * Test method for Attenuators. - * Return the number of test failed. - */ - pub fn test(&mut self) -> Result> { - // Test attenuators by getting back the attenuation - let mut error_count = 0; - // Convert cached SPI data into attenuation floats - let att_floats :[f32; 4] = [ - ((self.data[3] ^ 0xFC) as f32) / 8.0, - ((self.data[2] ^ 0xFC) as f32) / 8.0, - ((self.data[1] ^ 0xFC) as f32) / 8.0, - ((self.data[0] ^ 0xFC) as f32) / 8.0, - ]; - // Set the attenuation to an arbitrary value, then read the attenuation - self.set_attenuation([ - 3.5, 9.5, 20.0, 28.5 - ])?; - match self.get_attenuation() { - Ok(arr) => { - if arr[0] != 3.5 { - error_count += 1; - } - if arr[1] != 9.5 { - error_count += 1; - } - if arr[2] != 20.0 { - error_count += 1; - } - if arr[3] != 28.5 { - error_count += 1; - } - }, - Err(_) => return Err(Error::AttenuatorError), - }; - self.set_attenuation(att_floats)?; - Ok(error_count) - } + /* + * Test method for Attenuators. + * Return the number of test failed. + */ + pub fn test(&mut self) -> Result> { + // Test attenuators by getting back the attenuation + let mut error_count = 0; + // Convert cached SPI data into attenuation floats + let att_floats :[f32; 4] = [ + ((self.data[3] ^ 0xFC) as f32) / 8.0, + ((self.data[2] ^ 0xFC) as f32) / 8.0, + ((self.data[1] ^ 0xFC) as f32) / 8.0, + ((self.data[0] ^ 0xFC) as f32) / 8.0, + ]; + // Set the attenuation to an arbitrary value, then read the attenuation + self.set_attenuation([ + 3.5, 9.5, 20.0, 28.5 + ])?; + match self.get_attenuation() { + Ok(arr) => { + if arr[0] != 3.5 { + error_count += 1; + } + if arr[1] != 9.5 { + error_count += 1; + } + if arr[2] != 20.0 { + error_count += 1; + } + if arr[3] != 28.5 { + error_count += 1; + } + }, + Err(_) => return Err(Error::AttenuatorError), + }; + self.set_attenuation(att_floats)?; + Ok(error_count) + } } impl Transfer for Attenuator where - SPI: Transfer + SPI: Transfer { - type Error = Error; + type Error = Error; - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.spi.transfer(words).map_err(Error::SPI) - } + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.spi.transfer(words).map_err(Error::SPI) + } } diff --git a/src/bitmask_macro.rs b/src/bitmask_macro.rs index 1c7a574..0fca156 100644 --- a/src/bitmask_macro.rs +++ b/src/bitmask_macro.rs @@ -7,51 +7,51 @@ */ macro_rules! construct_bitmask { - ($collection: ident; $unsigned_type: ty; $($name: ident, $shift: expr, $width: expr),+) => { - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - #[allow(non_camel_case_types)] - pub enum $collection { - $( - $name, - )* - } + ($collection: ident; $unsigned_type: ty; $($name: ident, $shift: expr, $width: expr),+) => { + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + #[allow(non_camel_case_types)] + pub enum $collection { + $( + $name, + )* + } - impl $collection { - pub(crate) fn get_width(self) -> u8 { - match self { - $( - $collection::$name => $width, - )* - } - } - pub(crate) fn get_shift(self) -> u8 { - match self { - $( - $collection::$name => $shift, - )* - } - } - pub(crate) fn get_bitmask(self) -> $unsigned_type { - let mut mask: $unsigned_type = 0; - for bit in 0..self.get_width() { - mask |= (1 << (self.get_shift() + bit) % ((size_of::<$unsigned_type>() as u8) * 8)); - } - mask - } - pub(crate) fn get_shifted_bits(self, arg: $unsigned_type) -> $unsigned_type { - assert!(arg < (2 << self.get_width())); - (arg << (self.get_shift() % ((size_of::<$unsigned_type>() as u8) * 8))) - } - #[allow(dead_code)] - pub(crate) fn set_data_by_arg(self, data: &mut $unsigned_type, arg: $unsigned_type) { - // Clear bits in field, then insert shifted argument - *data &= (!self.get_bitmask()); - *data |= self.get_shifted_bits(arg); - } - pub(crate) fn get_filtered_content(self, data: $unsigned_type) -> $unsigned_type { - // Filter everything then shift bits - ((data & self.get_bitmask()) >> (self.get_shift() % ((size_of::<$unsigned_type>() as u8) * 8))) - } - } - } + impl $collection { + pub(crate) fn get_width(self) -> u8 { + match self { + $( + $collection::$name => $width, + )* + } + } + pub(crate) fn get_shift(self) -> u8 { + match self { + $( + $collection::$name => $shift, + )* + } + } + pub(crate) fn get_bitmask(self) -> $unsigned_type { + let mut mask: $unsigned_type = 0; + for bit in 0..self.get_width() { + mask |= (1 << (self.get_shift() + bit) % ((size_of::<$unsigned_type>() as u8) * 8)); + } + mask + } + pub(crate) fn get_shifted_bits(self, arg: $unsigned_type) -> $unsigned_type { + assert!(arg < (2 << self.get_width())); + (arg << (self.get_shift() % ((size_of::<$unsigned_type>() as u8) * 8))) + } + #[allow(dead_code)] + pub(crate) fn set_data_by_arg(self, data: &mut $unsigned_type, arg: $unsigned_type) { + // Clear bits in field, then insert shifted argument + *data &= (!self.get_bitmask()); + *data |= self.get_shifted_bits(arg); + } + pub(crate) fn get_filtered_content(self, data: $unsigned_type) -> $unsigned_type { + // Filter everything then shift bits + ((data & self.get_bitmask()) >> (self.get_shift() % ((size_of::<$unsigned_type>() as u8) * 8))) + } + } + } } diff --git a/src/config_register.rs b/src/config_register.rs index d6ff0f4..879bd87 100644 --- a/src/config_register.rs +++ b/src/config_register.rs @@ -4,121 +4,121 @@ use core::mem::size_of; // Bitmasks for CFG construct_bitmask!(CFGMask; u32; - RF_SW, 0, 4, - LED, 4, 4, - PROFILE, 8, 3, - IO_UPDATE, 12, 1, - MASK_NU, 13, 4, - CLK_SEL0, 17, 1, - SYNC_SEL, 18, 1, - RST, 19, 1, - IO_RST, 20, 1, - CLK_SEL1, 21, 1, - DIV, 22, 2 + RF_SW, 0, 4, + LED, 4, 4, + PROFILE, 8, 3, + IO_UPDATE, 12, 1, + MASK_NU, 13, 4, + CLK_SEL0, 17, 1, + SYNC_SEL, 18, 1, + RST, 19, 1, + IO_RST, 20, 1, + CLK_SEL1, 21, 1, + DIV, 22, 2 ); // BitMasks for CFG read construct_bitmask!(StatusMask; u32; - RF_SW, 0, 4, - SMP_ERR, 4, 4, - PLL_LOCK, 8, 4, - IFC_MODE, 12, 4, - PROTO_KEY, 16, 7 + RF_SW, 0, 4, + SMP_ERR, 4, 4, + PLL_LOCK, 8, 4, + IFC_MODE, 12, 4, + PROTO_KEY, 16, 7 ); pub struct ConfigRegister { - spi: SPI, - data: u32, + spi: SPI, + data: u32, } impl ConfigRegister where - SPI: Transfer + SPI: Transfer { - pub fn new(spi: SPI) -> Self { - ConfigRegister { - spi, - data: 0, - } - } + pub fn new(spi: SPI) -> Self { + ConfigRegister { + spi, + data: 0, + } + } - /* - * Set configuration bits according to data field - * Return status - */ - fn set_all_configurations(&mut self) -> Result> { - match self.spi.transfer(&mut [ - ((self.data & 0x00FF0000) >> 16) as u8, - ((self.data & 0x0000FF00) >> 8) as u8, - ((self.data & 0x000000FF) >> 0) as u8, - ]).map_err(Error::SPI) { - Ok(arr) => Ok( - ((arr[0] as u32) << 16) | - ((arr[1] as u32) << 8) | - arr[2] as u32 - ), - Err(e) => Err(e), - } - } + /* + * Set configuration bits according to data field + * Return status + */ + fn set_all_configurations(&mut self) -> Result> { + match self.spi.transfer(&mut [ + ((self.data & 0x00FF0000) >> 16) as u8, + ((self.data & 0x0000FF00) >> 8) as u8, + ((self.data & 0x000000FF) >> 0) as u8, + ]).map_err(Error::SPI) { + Ok(arr) => Ok( + ((arr[0] as u32) << 16) | + ((arr[1] as u32) << 8) | + arr[2] as u32 + ), + Err(e) => Err(e), + } + } - /* - * Set configuration bits according to supplied configs - * Return status - */ - pub fn set_configurations(&mut self, configs: &mut[(CFGMask, u32)]) -> Result> { - for config in configs.into_iter() { - config.0.set_data_by_arg(&mut self.data, config.1) - } - // Write all configurations at the same time - self.set_all_configurations() - } + /* + * Set configuration bits according to supplied configs + * Return status + */ + pub fn set_configurations(&mut self, configs: &mut[(CFGMask, u32)]) -> Result> { + for config in configs.into_iter() { + config.0.set_data_by_arg(&mut self.data, config.1) + } + // Write all configurations at the same time + self.set_all_configurations() + } - /* - * Return selected configuration field - */ - pub fn get_configuration(&mut self, config_type: CFGMask) -> u8 { - config_type.get_filtered_content(self.data) as u8 - } + /* + * Return selected configuration field + */ + pub fn get_configuration(&mut self, config_type: CFGMask) -> u8 { + config_type.get_filtered_content(self.data) as u8 + } - /* - * Return status using mask - */ - pub fn get_status(&mut self, status_type: StatusMask) -> Result> { - match self.set_all_configurations() { - Ok(val) => Ok(status_type.get_filtered_content(val) as u8), - Err(e) => Err(e), - } - } + /* + * Return status using mask + */ + pub fn get_status(&mut self, status_type: StatusMask) -> Result> { + match self.set_all_configurations() { + Ok(val) => Ok(status_type.get_filtered_content(val) as u8), + Err(e) => Err(e), + } + } - /* - * Return entire status register - */ - pub fn get_all_status(&mut self) -> Result> { - return self.set_all_configurations(); - } + /* + * Return entire status register + */ + pub fn get_all_status(&mut self) -> Result> { + return self.set_all_configurations(); + } - /* - * Test method for Configuration Register. - * Return the number of test failed. - */ - pub fn test(&mut self) -> Result> { - // Test configuration register by getting PROTO_KEY. - match self.get_status(StatusMask::PROTO_KEY) { - Ok(8) => Ok(0), - Ok(_) => Ok(1), - Err(_) => Err(Error::ConfigRegisterError), - } - } + /* + * Test method for Configuration Register. + * Return the number of test failed. + */ + pub fn test(&mut self) -> Result> { + // Test configuration register by getting PROTO_KEY. + match self.get_status(StatusMask::PROTO_KEY) { + Ok(8) => Ok(0), + Ok(_) => Ok(1), + Err(_) => Err(Error::ConfigRegisterError), + } + } } impl Transfer for ConfigRegister where - SPI: Transfer + SPI: Transfer { - type Error = Error; + type Error = Error; - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.spi.transfer(words).map_err(Error::SPI) - } + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.spi.transfer(words).map_err(Error::SPI) + } } diff --git a/src/cpld.rs b/src/cpld.rs index 8fe4f9c..0429693 100644 --- a/src/cpld.rs +++ b/src/cpld.rs @@ -2,14 +2,14 @@ use crate::urukul::Error; use crate::spi_slave::Parts; use embedded_hal::{ - digital::v2::OutputPin, - blocking::spi::Transfer, + digital::v2::OutputPin, + blocking::spi::Transfer, }; use core::cell; /* - * Basic structure for CPLD signal multiplexing + * Basic structure for CPLD signal multiplexing */ #[derive(Debug)] pub struct CPLDData { diff --git a/src/dds.rs b/src/dds.rs index 9a27ce6..f8d3457 100644 --- a/src/dds.rs +++ b/src/dds.rs @@ -9,807 +9,807 @@ use heapless::consts::*; * Bitmask for all configurations (Order: CFR3, CFR2, CFR1) */ construct_bitmask!(DDSCFRMask; u32; - // CFR1 bitmasks - LSB_FIRST, 0, 1, - SDIO_IN_ONLY, 1, 1, - EXT_POWER_DOWN_CTRL, 3, 1, - AUX_DAC_POWER_DOWN, 4, 1, - REFCLK_IN_POWER_DOWN, 5, 1, - DAC_POWER_DOWN, 6, 1, - DIGITAL_POWER_DOWN, 7, 1, - SEL_AUTO_OSK, 8, 1, - OSK_ENABLE, 9, 1, - LOAD_ARR_IO_UPDATE, 10, 1, - CLEAR_PHASE_ACU, 11, 1, - CLEAR_DIGITAL_RAMP_ACU, 12, 1, - AUTOCLEAR_PHASE_ACU, 13, 1, - AUTOCLEAR_DIGITAL_RAMP_ACU, 14, 1, - LOAD_LRR_IO_UPDATE, 15, 1, - SEL_DDS_SIN_OUT, 16, 1, - PROFILE_CTRL, 17, 4, - INV_SINC_FILTER_ENABLE, 22, 1, - MANUAL_OSK_EXT_CTRL, 23, 1, - RAM_PLAYBACK_DST, 29, 2, - RAM_ENABLE, 31, 1, + // CFR1 bitmasks + LSB_FIRST, 0, 1, + SDIO_IN_ONLY, 1, 1, + EXT_POWER_DOWN_CTRL, 3, 1, + AUX_DAC_POWER_DOWN, 4, 1, + REFCLK_IN_POWER_DOWN, 5, 1, + DAC_POWER_DOWN, 6, 1, + DIGITAL_POWER_DOWN, 7, 1, + SEL_AUTO_OSK, 8, 1, + OSK_ENABLE, 9, 1, + LOAD_ARR_IO_UPDATE, 10, 1, + CLEAR_PHASE_ACU, 11, 1, + CLEAR_DIGITAL_RAMP_ACU, 12, 1, + AUTOCLEAR_PHASE_ACU, 13, 1, + AUTOCLEAR_DIGITAL_RAMP_ACU, 14, 1, + LOAD_LRR_IO_UPDATE, 15, 1, + SEL_DDS_SIN_OUT, 16, 1, + PROFILE_CTRL, 17, 4, + INV_SINC_FILTER_ENABLE, 22, 1, + MANUAL_OSK_EXT_CTRL, 23, 1, + RAM_PLAYBACK_DST, 29, 2, + RAM_ENABLE, 31, 1, - // CFR2 bitmasks - FM_GAIN, 0 +32, 4, - PARALLEL_DATA_PORT_ENABLE, 4 +32, 1, - SYNC_TIM_VALIDATION_DISABLE, 5 +32, 1, - DATA_ASSEM_HOLD_LAST_VALUE, 6 +32, 1, - MATCHED_LATENCY_ENABLE, 7 +32, 1, - TXENABLE_INV, 9 +32, 1, - PDCLK_INV, 10 +32, 1, - PDCLK_ENABLE, 11 +32, 1, - IO_UPDATE_RATE_CTRL, 14 +32, 2, - READ_EFFECTIVE_FTW, 16 +32, 1, - DIGITAL_RAMP_NO_DWELL_LOW, 17 +32, 1, - DIGITAL_RAMP_NO_DWELL_HIGH, 18 +32, 1, - DIGITAL_RAMP_ENABLE, 19 +32, 1, - DIGITAL_RAMP_DEST, 20 +32, 2, - SYNC_CLK_ENABLE, 22 +32, 1, - INT_IO_UPDATE_ACTIVE, 23 +32, 1, - EN_AMP_SCALE_SINGLE_TONE_PRO, 24 +32, 1, + // CFR2 bitmasks + FM_GAIN, 0 +32, 4, + PARALLEL_DATA_PORT_ENABLE, 4 +32, 1, + SYNC_TIM_VALIDATION_DISABLE, 5 +32, 1, + DATA_ASSEM_HOLD_LAST_VALUE, 6 +32, 1, + MATCHED_LATENCY_ENABLE, 7 +32, 1, + TXENABLE_INV, 9 +32, 1, + PDCLK_INV, 10 +32, 1, + PDCLK_ENABLE, 11 +32, 1, + IO_UPDATE_RATE_CTRL, 14 +32, 2, + READ_EFFECTIVE_FTW, 16 +32, 1, + DIGITAL_RAMP_NO_DWELL_LOW, 17 +32, 1, + DIGITAL_RAMP_NO_DWELL_HIGH, 18 +32, 1, + DIGITAL_RAMP_ENABLE, 19 +32, 1, + DIGITAL_RAMP_DEST, 20 +32, 2, + SYNC_CLK_ENABLE, 22 +32, 1, + INT_IO_UPDATE_ACTIVE, 23 +32, 1, + EN_AMP_SCALE_SINGLE_TONE_PRO, 24 +32, 1, - // CFR3 bitmasks - N, 1 +64, 7, - PLL_ENABLE, 8 +64, 1, - PFD_RESET, 10 +64, 1, - REFCLK_IN_DIV_RESETB, 14 +64, 1, - REFCLK_IN_DIV_BYPASS, 15 +64, 1, - I_CP, 19 +64, 3, - VCO_SEL, 24 +64, 3, - DRV0, 28 +64, 2 + // CFR3 bitmasks + N, 1 +64, 7, + PLL_ENABLE, 8 +64, 1, + PFD_RESET, 10 +64, 1, + REFCLK_IN_DIV_RESETB, 14 +64, 1, + REFCLK_IN_DIV_BYPASS, 15 +64, 1, + I_CP, 19 +64, 3, + VCO_SEL, 24 +64, 3, + DRV0, 28 +64, 2 ); -const WRITE_MASK :u8 = 0x00; -const READ_MASK :u8 = 0x80; +const WRITE_MASK :u8 = 0x00; +const READ_MASK :u8 = 0x80; static mut RAM_VEC: Vec = Vec(heapless::i::Vec::new()); #[derive(Clone, PartialEq)] pub enum RAMDestination { - Frequency = 0, - Phase = 1, - Amplitude = 2, - Polar = 3, + Frequency = 0, + Phase = 1, + Amplitude = 2, + Polar = 3, } #[derive(Clone)] pub enum RAMOperationMode { - DirectSwitch = 0, - RampUp = 1, - BidirectionalRamp = 2, - ContinuousBidirectionalRamp = 3, - ContinuousRecirculate = 4, + DirectSwitch = 0, + RampUp = 1, + BidirectionalRamp = 2, + ContinuousBidirectionalRamp = 3, + ContinuousRecirculate = 4, } pub struct DDS { - spi: SPI, - f_ref_clk: f64, - f_sys_clk: f64, + spi: SPI, + f_ref_clk: f64, + f_sys_clk: f64, } impl DDS where - SPI: Transfer + SPI: Transfer { - pub fn new(spi: SPI, f_ref_clk: f64) -> Self { - DDS { - spi, - f_ref_clk, - f_sys_clk: f_ref_clk, - } - } + pub fn new(spi: SPI, f_ref_clk: f64) -> Self { + DDS { + spi, + f_ref_clk, + f_sys_clk: f_ref_clk, + } + } } impl Transfer for DDS where - SPI: Transfer + SPI: Transfer { - type Error = Error; + type Error = Error; - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - self.spi.transfer(words).map_err(Error::SPI) - } + fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { + self.spi.transfer(words).map_err(Error::SPI) + } } impl DDS where - SPI: Transfer + SPI: Transfer { - /* - * Implement init: Set SDIO to be input only, using LSB first - */ - pub fn init(&mut self) -> Result<(), Error> { - match self.write_register(0x00, &mut [ - 0x00, 0x00, 0x00, 0x02 - ]) { - Ok(_) => Ok(()), - Err(e) => Err(e), - } - } - - /* - * Implement clock control - */ - pub fn enable_divided_ref_clk(&mut self) -> Result<(), Error> { - self.set_configurations(&mut [ - // Disable PLL - (DDSCFRMask::PLL_ENABLE, 0), - // Take ref_clk source from divider - (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0), - // Ensure divider is not reset - (DDSCFRMask::REFCLK_IN_DIV_RESETB, 1), - ])?; - self.f_sys_clk = self.f_ref_clk / 2.0; - Ok(()) - } - - pub fn enable_normal_ref_clk(&mut self) -> Result<(), Error> { - self.set_configurations(&mut [ - // Disable PLL - (DDSCFRMask::PLL_ENABLE, 0), - // Take ref_clk source from divider bypass - (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 1), - // Reset does not matter - (DDSCFRMask::REFCLK_IN_DIV_RESETB, 1), - ])?; - self.f_sys_clk = self.f_ref_clk; - Ok(()) - } - - pub fn enable_pll(&mut self, f_sys_clk: f64) -> Result<(), Error> { - // Get a divider - let divider = (f_sys_clk / self.f_ref_clk) as u64; - // Reject extreme divider values. However, accept no frequency division - if (divider > 127 || divider < 12) && divider != 1 { - // panic!("Invalid divider value for PLL!"); - return Err(Error::DDSCLKError); - } - let vco = self.get_VCO_no(f_sys_clk, divider as u8)?; - - self.set_configurations(&mut [ - // Enable PLL, set divider (valid or not) and VCO - (DDSCFRMask::PLL_ENABLE, 1), - (DDSCFRMask::N, divider as u32), - (DDSCFRMask::VCO_SEL, vco.into()), - // Reset PLL lock before re-enabling it - (DDSCFRMask::PFD_RESET, 1), - ])?; - self.set_configurations(&mut [ - (DDSCFRMask::PFD_RESET, 0), - ])?; - self.f_sys_clk = self.f_ref_clk * (divider as f64); - Ok(()) - } - - // Change external clock source (ref_clk) - pub fn set_ref_clk_frequency(&mut self, f_ref_clk: f64) -> Result<(), Error> { - // Override old reference clock frequency (ref_clk) - self.f_ref_clk = f_ref_clk; - - // Calculate the new system clock frequency, examine the clock tree - let mut configuration_queries = [ - // Acquire PLL status - (DDSCFRMask::PLL_ENABLE, 0), - // Acquire N-divider, to adjust VCO if necessary - (DDSCFRMask::N, 0), - // Acquire REF_CLK divider bypass - (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0) - ]; - - self.get_configurations(&mut configuration_queries)?; - if configuration_queries[0].1 == 1 { - // Recalculate sys_clk - let divider :f64 = configuration_queries[1].1.into(); - let f_sys_clk = self.f_ref_clk * divider; - // Adjust VCO - match self.get_VCO_no(f_sys_clk, divider as u8) { - Ok(vco_no) => { - self.set_configurations(&mut [ - // Update VCO selection - (DDSCFRMask::VCO_SEL, vco_no.into()), - // Reset PLL lock before re-enabling it - (DDSCFRMask::PFD_RESET, 1), - ])?; - self.set_configurations(&mut [ - (DDSCFRMask::PFD_RESET, 0), - ])?; - // Update f_sys_clk from recalculation - self.f_sys_clk = f_sys_clk; - Ok(()) - }, - Err(_) => { - // Forcibly turn off PLL, enable default clk tree (divide by 2) - self.enable_divided_ref_clk() - } - } - } - else if configuration_queries[2].1 == 0 { - self.f_sys_clk = self.f_ref_clk / 2.0; - Ok(()) - } - else { - self.f_sys_clk = self.f_ref_clk; - Ok(()) - } - } - - // Change sys_clk frequency, method to be determined - pub fn set_sys_clk_frequency(&mut self, f_sys_clk: f64) -> Result<(), Error> { - // If f_sys_clk is exactly the same as f_ref_clk, then invoke enable_normal_ref_clk - if f_sys_clk == self.f_ref_clk { - self.enable_normal_ref_clk() - } - // Otherwise, if the requested sys_clk is half of ref_clk, invoke enable_divided_ref_clk - else if f_sys_clk == (self.f_ref_clk / 2.0) { - self.enable_divided_ref_clk() - } - // Finally, try enabling PLL - else { - self.enable_pll(f_sys_clk) - } - } - - #[allow(non_snake_case)] - fn get_VCO_no(&mut self, f_sys_clk: f64, divider: u8) -> Result> { - // Select a VCO - if divider == 1 { - Ok(6) // Bypass PLL if no frequency division needed - } else if f_sys_clk > 1_150_000_000.0 { - Err(Error::DDSCLKError) - } else if f_sys_clk > 820_000_000.0 { - Ok(5) - } else if f_sys_clk > 700_000_000.0 { - Ok(4) - } else if f_sys_clk > 600_000_000.0 { - Ok(3) - } else if f_sys_clk > 500_000_000.0 { - Ok(2) - } else if f_sys_clk > 420_000_000.0 { - Ok(1) - } else if f_sys_clk > 370_000_000.0 { - Ok(0) - } else { - Ok(7) // Bypass PLL if f_sys_clk is too low - } - } - - /* - * Implement configurations registers I/O through bitmasks - * - * Get all (cfr1, cfr2, cfr3) contents - */ - fn get_all_configurations(&mut self) -> Result<[u32; 3], Error> { - let mut cfr_reg = [0; 12]; - self.read_register(0x00, &mut cfr_reg[0..4])?; - self.read_register(0x01, &mut cfr_reg[4..8])?; - self.read_register(0x02, &mut cfr_reg[8..12])?; - Ok([ - (cfr_reg[0] as u32) << 24 | (cfr_reg[1] as u32) << 16 | (cfr_reg[2] as u32) << 8 | (cfr_reg[3] as u32), - (cfr_reg[4] as u32) << 24 | (cfr_reg[5] as u32) << 16 | (cfr_reg[6] as u32) << 8 | (cfr_reg[7] as u32), - (cfr_reg[8] as u32) << 24 | (cfr_reg[9] as u32) << 16 | (cfr_reg[10] as u32) << 8 | (cfr_reg[11] as u32) - ]) - } - - /* - * Get a set of configurations using DDSCFRMask - */ - pub fn get_configurations<'w>(&mut self, mask_pairs: &'w mut[(DDSCFRMask, u32)]) -> Result<&'w [(DDSCFRMask, u32)], Error> { - let data_array = self.get_all_configurations()?; - for index in 0..mask_pairs.len() { - mask_pairs[index].1 = match mask_pairs[index].0.get_shift() { - 0..=31 => mask_pairs[index].0.get_filtered_content(data_array[0]), - 32..=63 => mask_pairs[index].0.get_filtered_content(data_array[1]), - 64..=95 => mask_pairs[index].0.get_filtered_content(data_array[2]), - _ => panic!("Invalid DDSCFRMask!"), - } - } - Ok(mask_pairs) - } - - /* - * Write (cfr1, cfr2, cfr3) contents - */ - fn set_all_configurations(&mut self, data_array: [u32; 3]) -> Result<(), Error> { - for register in 0x00..=0x02 { - self.write_register(register, &mut [ - ((data_array[register as usize] >> 24) & 0xFF) as u8, - ((data_array[register as usize] >> 16) & 0xFF) as u8, - ((data_array[register as usize] >> 8 ) & 0xFF) as u8, - ((data_array[register as usize] >> 0 ) & 0xFF) as u8 - ])?; - } - Ok(()) - } - - /* - * Set a set of configurations using DDSCFRMask - */ - pub fn set_configurations(&mut self, mask_pairs: &mut[(DDSCFRMask, u32)]) -> Result<(), Error> { - let mut data_array = self.get_all_configurations()?; - for index in 0..mask_pairs.len() { - // Reject any attempt to rewrite LSB_FIRST and SBIO_INPUT_ONLY - if mask_pairs[index].0 == DDSCFRMask::LSB_FIRST || mask_pairs[index].0 == DDSCFRMask::SDIO_IN_ONLY { - continue; - } - match mask_pairs[index].0.get_shift() { - 0..=31 => mask_pairs[index].0.set_data_by_arg(&mut data_array[0], mask_pairs[index].1), - 32..=63 => mask_pairs[index].0.set_data_by_arg(&mut data_array[1], mask_pairs[index].1), - 64..=95 => mask_pairs[index].0.set_data_by_arg(&mut data_array[2], mask_pairs[index].1), - _ => panic!("Invalid DDSCFRMask!"), - }; - } - self.set_all_configurations(data_array.clone()) - } - - /* - * Setup a complete single tone profile - * Phase: Expressed in positive degree, i.e. [0.0, 360.0) - * Frequency: Must be non-negative - * Amplitude: In a scale from 0 to 1, taking float - */ - pub fn set_single_tone_profile(&mut self, profile: u8, f_out: f64, phase_offset: f64, amp_scale_factor: f64) -> Result<(), Error> { - - assert!(profile < 8); - assert!(f_out >= 0.0); - assert!(phase_offset >= 0.0 && phase_offset < 360.0); - assert!(amp_scale_factor >=0.0 && amp_scale_factor <= 1.0); - - let ftw = self.frequency_to_ftw(f_out); - let pow = self.degree_to_pow(phase_offset); - let asf = self.amplitude_to_asf(amp_scale_factor); - - // Setup configuration registers before writing single tone register - self.enable_single_tone_configuration()?; - - // Transfer single tone profile data - self.write_register(0x0E + profile, &mut [ - ((asf >> 8 ) & 0xFF) as u8, - ((asf >> 0 ) & 0xFF) as u8, - ((pow >> 8 ) & 0xFF) as u8, - ((pow >> 0 ) & 0xFF) as u8, - ((ftw >> 24) & 0xFF) as u8, - ((ftw >> 16) & 0xFF) as u8, - ((ftw >> 8 ) & 0xFF) as u8, - ((ftw >> 0 ) & 0xFF) as u8, - ]) - } - - /* - * Set frequency of a single tone profile - * Frequency: Must be non-negative - * Keep other field unchanged in the register - */ - pub fn set_single_tone_profile_frequency(&mut self, profile: u8, f_out: f64) -> Result<(), Error> { - - // Setup configuration registers before writing single tone register - self.enable_single_tone_configuration()?; - - let ftw = self.frequency_to_ftw(f_out); - - // Read existing amplitude/phase data - let mut register: [u8; 8] = [0; 8]; - self.read_register(0x0E + profile, &mut register)?; - - // Overwrite FTW - register[4] = ((ftw >> 24) & 0xFF) as u8; - register[5] = ((ftw >> 16) & 0xFF) as u8; - register[6] = ((ftw >> 8) & 0xFF) as u8; - register[7] = ((ftw >> 0) & 0xFF) as u8; - - // Update FTW by writing back the register - self.write_register(0x0E + profile, &mut register) - } - - /* - * Set phase offset of a single tone profile - * Phase: Expressed in positive degree, i.e. [0.0, 360.0) - * Keep other field unchanged in the register - */ - pub fn set_single_tone_profile_phase(&mut self, profile: u8, phase_offset: f64) -> Result<(), Error> { - - // Setup configuration registers before writing single tone register - self.enable_single_tone_configuration()?; - - let pow = self.degree_to_pow(phase_offset); - - // Read existing amplitude/frequency data - let mut register: [u8; 8] = [0; 8]; - self.read_register(0x0E + profile, &mut register)?; - - // Overwrite POW - register[2] = ((pow >> 8) & 0xFF) as u8; - register[3] = ((pow >> 0) & 0xFF) as u8; - - // Update POW by writing back the register - self.write_register(0x0E + profile, &mut register) - } - - /* - * Set amplitude offset of a single tone profile - * Amplitude: In a scale from 0 to 1, taking float - * Keep other field unchanged in the register - */ - pub fn set_single_tone_profile_amplitude(&mut self, profile: u8, amp_scale_factor: f64) -> Result<(), Error> { - - // Setup configuration registers before writing single tone register - self.enable_single_tone_configuration()?; - - // Calculate amplitude_scale_factor (ASF) - let asf = self.amplitude_to_asf(amp_scale_factor); - - // Read existing frequency/phase data - let mut register: [u8; 8] = [0; 8]; - self.read_register(0x0E + profile, &mut register)?; - - // Overwrite POW - register[0] = ((asf >> 8) & 0xFF) as u8; - register[1] = ((asf >> 0) & 0xFF) as u8; - - // Update POW by writing back the register - self.write_register(0x0E + profile, &mut register) - } - - // Helper function to switch into single tone mode - // Need to setup configuration registers before writing single tone register - fn enable_single_tone_configuration(&mut self) -> Result<(), Error> { - - self.set_configurations(&mut [ - (DDSCFRMask::RAM_ENABLE, 0), - (DDSCFRMask::DIGITAL_RAMP_ENABLE, 0), - (DDSCFRMask::OSK_ENABLE, 0), - (DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0), - ])?; - self.set_configurations(&mut [ - (DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1), - ]) - } - - // Helper function to switch into RAM mode - // Need to setup configuration registers before writing into RAM profile register - fn enable_ram_configuration(&mut self, ram_dst: RAMDestination) -> Result<(), Error> { - self.set_configurations(&mut [ - (DDSCFRMask::RAM_ENABLE, 1), - (DDSCFRMask::RAM_PLAYBACK_DST, ram_dst as u32), - ]) - } - - // Helper function to switch out of RAM mode - // Need to setup configuration registers before writing into RAM profile register - fn disable_ram_configuration(&mut self) -> Result<(), Error> { - self.set_configurations(&mut [ - (DDSCFRMask::RAM_ENABLE, 0), - ]) - } - - /* - * Configure a RAM mode profile, wrt supplied frequency data - * This will setup the static RAM_VEC by converting frequency to ftw - */ - pub unsafe fn set_frequency_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, - no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, - frequency_data: &[f64] - ) -> Result<(), Error> { - - // Check the legality of the profile setup - assert!(profile <= 7); - assert!(end_addr >= start_addr); - assert!(end_addr < 1024); - assert_eq!(frequency_data.len() as u16, end_addr - start_addr + 1); - - // Clear RAM vector, and add address byte - RAM_VEC.clear(); - RAM_VEC.push(0x16) - .map_err(|_| Error::DDSRAMError)?; - - // Convert frequency data into bytes recognized by DDS - for freq in frequency_data.iter() { - let ftw = self.frequency_to_ftw(*freq); - RAM_VEC.push(((ftw >> 24) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((ftw >> 16) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((ftw >> 8) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((ftw >> 0) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - } - - self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Frequency, - no_dwell_high, zero_crossing, op_mode, playback_rate) - } - - /* - * Configure a RAM mode profile, wrt supplied amplitude data - * This will setup the static RAM_VEC by converting amplitude to asf - */ - pub unsafe fn set_amplitude_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, - no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, - amplitude_data: &[f64] - ) -> Result<(), Error> { - - // Check the legality of the profile setup - assert!(profile <= 7); - assert!(end_addr >= start_addr); - assert!(end_addr < 1024); - assert_eq!(amplitude_data.len() as u16, end_addr - start_addr + 1); - - // Clear RAM vector, and add address byte - RAM_VEC.clear(); - RAM_VEC.push(0x16) - .map_err(|_| Error::DDSRAMError)?; - - // Convert amplitude data into bytes recognized by DDS - for amp in amplitude_data.iter() { - let asf = self.amplitude_to_asf(*amp); - RAM_VEC.push(((asf >> 8) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((asf << 2) & 0xFC) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(0) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(0) - .map_err(|_| Error::DDSRAMError)?; - } - - self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Amplitude, - no_dwell_high, zero_crossing, op_mode, playback_rate) - } - - /* - * Configure a RAM mode profile, wrt supplied phase data - * This will setup the static RAM_VEC by converting phase to ftw - */ - pub unsafe fn set_phase_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, - no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, - phase_data: &[f64] - ) -> Result<(), Error> { - - // Check the legality of the profile setup - assert!(profile <= 7); - assert!(end_addr >= start_addr); - assert!(end_addr < 1024); - assert_eq!(phase_data.len() as u16, end_addr - start_addr + 1); - - // Clear RAM vector, and add address byte - RAM_VEC.clear(); - RAM_VEC.push(0x16) - .map_err(|_| Error::DDSRAMError)?; - - // Convert phase data into bytes recognized by DDS - for deg in phase_data.iter() { - let pow = self.degree_to_pow(*deg); - RAM_VEC.push(((pow >> 8) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((pow >> 0) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(0) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(0) - .map_err(|_| Error::DDSRAMError)?; - } - - self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Phase, - no_dwell_high, zero_crossing, op_mode, playback_rate) - } - - /* - * Configure a RAM mode profile, wrt supplied phase data - * This will setup the static RAM_VEC by converting phase to ftw - */ - pub unsafe fn set_polar_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, - no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, - polar_data: &[(f64, f64)] - ) -> Result<(), Error> { - - // Check the legality of the profile setup - assert!(profile <= 7); - assert!(end_addr >= start_addr); - assert!(end_addr < 1024); - assert_eq!(polar_data.len() as u16, end_addr - start_addr + 1); - - // Clear RAM vector, and add address byte - RAM_VEC.clear(); - RAM_VEC.push(0x16) - .map_err(|_| Error::DDSRAMError)?; - - // Convert amplitude data into bytes recognized by DDS - for (deg, amp) in polar_data.iter() { - let pow = self.degree_to_pow(*deg); - let asf = self.amplitude_to_asf(*amp); - RAM_VEC.push(((pow >> 8) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((pow >> 0) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((asf >> 8) & 0xFF) as u8) - .map_err(|_| Error::DDSRAMError)?; - RAM_VEC.push(((asf << 2) & 0xFC) as u8) - .map_err(|_| Error::DDSRAMError)?; - } - - self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Phase, - no_dwell_high, zero_crossing, op_mode, playback_rate) - } - - /* - * Configure a RAM mode profile, w.r.t static vector (RAM_VEC) - */ - fn set_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, - ram_dst: RAMDestination, no_dwell_high: bool, zero_crossing: bool, - op_mode: RAMOperationMode, playback_rate: f64 - ) -> Result<(), Error> { - - // Check the legality of the profile setup - assert!(profile <= 7); - assert!(end_addr >= start_addr); - assert!(end_addr < 1024); - // assert_eq! RAM_VEC.len() as u16, ((end_addr - start_addr + 1) * 4) + 1); - - // Calculate address step rate, and check legality - let step_rate = (self.f_sys_clk/(4.0 * playback_rate)) as u64; - if step_rate == 0 || step_rate > 0xFFFF { - return Err(Error::DDSRAMError); - } - - // Before setting up RAM, disable RAM_ENABLE - self.enable_ram_configuration(ram_dst.clone())?; - - // Write a RAM profile, but include all data in RAM - self.write_register(0x0E + profile, &mut [ - 0x00, - ((step_rate >> 8) & 0xFF).try_into().unwrap(), - ((step_rate >> 0) & 0xFF).try_into().unwrap(), - ((end_addr >> 2) & 0xFF).try_into().unwrap(), - ((end_addr & 0x3) << 6).try_into().unwrap(), - ((start_addr >> 2) & 0xFF).try_into().unwrap(), - ((start_addr & 0x3) << 6).try_into().unwrap(), - ((no_dwell_high as u8) << 5) | ((zero_crossing as u8) << 3) | (op_mode as u8) - ])?; - - // Temporarily disable RAM mode while accessing into RAM - self.disable_ram_configuration()?; - unsafe { - self.write_ram()?; - } - - // Properly configure start_addr and end_addr - self.enable_ram_configuration(ram_dst) - - } - - // Calculate ftw (frequency tuning word) - fn frequency_to_ftw(&mut self, f_out: f64) -> u32 { - let f_res: u64 = 1 << 32; - ((f_res as f64) * f_out / self.f_sys_clk) as u32 - } - - // Calculate pow (Phase Offset Word) - fn degree_to_pow(&mut self, phase_offset: f64) -> u16 { - // Calculate phase offset word (POW) - let phase_res: u64 = 1 << 16; - ((phase_res as f64) * phase_offset / 360.0) as u16 - } - - // Calculate asf (Amplitude Scale Factor) - fn amplitude_to_asf(&mut self, amplitude: f64) -> u16 { - let amp_res: u64 = 0x3FFF; - ((amp_res as f64) * amplitude) as u16 - } - - // Write data in RAM - unsafe fn write_ram(&mut self) -> Result<(), Error> { - self.spi.transfer(&mut RAM_VEC) - .map(|_| ()) - .map_err(Error::SPI) - } - - /* - * Test method for DDS. - * Return the number of test failed. - */ - pub fn test(&mut self) -> Result> { - // Test configuration register by getting SDIO_IN_ONLY and LSB_FIRST. - let mut error_count = 0; - let mut config_checks = [ - (DDSCFRMask::SDIO_IN_ONLY, 1), - (DDSCFRMask::LSB_FIRST, 0) - ]; - self.get_configurations(&mut config_checks)?; - if config_checks[0].1 == 0 { - error_count += 1; - } - if config_checks[1].1 == 1 { - error_count += 1; - } - Ok(error_count) - } - - // Setter function for f_sys_clk - // Warning: This does not setup the chip to generate this actual f_sys_clk - pub(crate) fn set_f_sys_clk(&mut self, f_sys_clk: f64) { - self.f_sys_clk = f_sys_clk; - } - - // Getter function for f_sys_clk - pub fn get_f_sys_clk(&mut self) -> f64 { - self.f_sys_clk - } + /* + * Implement init: Set SDIO to be input only, using LSB first + */ + pub fn init(&mut self) -> Result<(), Error> { + match self.write_register(0x00, &mut [ + 0x00, 0x00, 0x00, 0x02 + ]) { + Ok(_) => Ok(()), + Err(e) => Err(e), + } + } + + /* + * Implement clock control + */ + pub fn enable_divided_ref_clk(&mut self) -> Result<(), Error> { + self.set_configurations(&mut [ + // Disable PLL + (DDSCFRMask::PLL_ENABLE, 0), + // Take ref_clk source from divider + (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0), + // Ensure divider is not reset + (DDSCFRMask::REFCLK_IN_DIV_RESETB, 1), + ])?; + self.f_sys_clk = self.f_ref_clk / 2.0; + Ok(()) + } + + pub fn enable_normal_ref_clk(&mut self) -> Result<(), Error> { + self.set_configurations(&mut [ + // Disable PLL + (DDSCFRMask::PLL_ENABLE, 0), + // Take ref_clk source from divider bypass + (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 1), + // Reset does not matter + (DDSCFRMask::REFCLK_IN_DIV_RESETB, 1), + ])?; + self.f_sys_clk = self.f_ref_clk; + Ok(()) + } + + pub fn enable_pll(&mut self, f_sys_clk: f64) -> Result<(), Error> { + // Get a divider + let divider = (f_sys_clk / self.f_ref_clk) as u64; + // Reject extreme divider values. However, accept no frequency division + if (divider > 127 || divider < 12) && divider != 1 { + // panic!("Invalid divider value for PLL!"); + return Err(Error::DDSCLKError); + } + let vco = self.get_VCO_no(f_sys_clk, divider as u8)?; + + self.set_configurations(&mut [ + // Enable PLL, set divider (valid or not) and VCO + (DDSCFRMask::PLL_ENABLE, 1), + (DDSCFRMask::N, divider as u32), + (DDSCFRMask::VCO_SEL, vco.into()), + // Reset PLL lock before re-enabling it + (DDSCFRMask::PFD_RESET, 1), + ])?; + self.set_configurations(&mut [ + (DDSCFRMask::PFD_RESET, 0), + ])?; + self.f_sys_clk = self.f_ref_clk * (divider as f64); + Ok(()) + } + + // Change external clock source (ref_clk) + pub fn set_ref_clk_frequency(&mut self, f_ref_clk: f64) -> Result<(), Error> { + // Override old reference clock frequency (ref_clk) + self.f_ref_clk = f_ref_clk; + + // Calculate the new system clock frequency, examine the clock tree + let mut configuration_queries = [ + // Acquire PLL status + (DDSCFRMask::PLL_ENABLE, 0), + // Acquire N-divider, to adjust VCO if necessary + (DDSCFRMask::N, 0), + // Acquire REF_CLK divider bypass + (DDSCFRMask::REFCLK_IN_DIV_BYPASS, 0) + ]; + + self.get_configurations(&mut configuration_queries)?; + if configuration_queries[0].1 == 1 { + // Recalculate sys_clk + let divider :f64 = configuration_queries[1].1.into(); + let f_sys_clk = self.f_ref_clk * divider; + // Adjust VCO + match self.get_VCO_no(f_sys_clk, divider as u8) { + Ok(vco_no) => { + self.set_configurations(&mut [ + // Update VCO selection + (DDSCFRMask::VCO_SEL, vco_no.into()), + // Reset PLL lock before re-enabling it + (DDSCFRMask::PFD_RESET, 1), + ])?; + self.set_configurations(&mut [ + (DDSCFRMask::PFD_RESET, 0), + ])?; + // Update f_sys_clk from recalculation + self.f_sys_clk = f_sys_clk; + Ok(()) + }, + Err(_) => { + // Forcibly turn off PLL, enable default clk tree (divide by 2) + self.enable_divided_ref_clk() + } + } + } + else if configuration_queries[2].1 == 0 { + self.f_sys_clk = self.f_ref_clk / 2.0; + Ok(()) + } + else { + self.f_sys_clk = self.f_ref_clk; + Ok(()) + } + } + + // Change sys_clk frequency, method to be determined + pub fn set_sys_clk_frequency(&mut self, f_sys_clk: f64) -> Result<(), Error> { + // If f_sys_clk is exactly the same as f_ref_clk, then invoke enable_normal_ref_clk + if f_sys_clk == self.f_ref_clk { + self.enable_normal_ref_clk() + } + // Otherwise, if the requested sys_clk is half of ref_clk, invoke enable_divided_ref_clk + else if f_sys_clk == (self.f_ref_clk / 2.0) { + self.enable_divided_ref_clk() + } + // Finally, try enabling PLL + else { + self.enable_pll(f_sys_clk) + } + } + + #[allow(non_snake_case)] + fn get_VCO_no(&mut self, f_sys_clk: f64, divider: u8) -> Result> { + // Select a VCO + if divider == 1 { + Ok(6) // Bypass PLL if no frequency division needed + } else if f_sys_clk > 1_150_000_000.0 { + Err(Error::DDSCLKError) + } else if f_sys_clk > 820_000_000.0 { + Ok(5) + } else if f_sys_clk > 700_000_000.0 { + Ok(4) + } else if f_sys_clk > 600_000_000.0 { + Ok(3) + } else if f_sys_clk > 500_000_000.0 { + Ok(2) + } else if f_sys_clk > 420_000_000.0 { + Ok(1) + } else if f_sys_clk > 370_000_000.0 { + Ok(0) + } else { + Ok(7) // Bypass PLL if f_sys_clk is too low + } + } + + /* + * Implement configurations registers I/O through bitmasks + * + * Get all (cfr1, cfr2, cfr3) contents + */ + fn get_all_configurations(&mut self) -> Result<[u32; 3], Error> { + let mut cfr_reg = [0; 12]; + self.read_register(0x00, &mut cfr_reg[0..4])?; + self.read_register(0x01, &mut cfr_reg[4..8])?; + self.read_register(0x02, &mut cfr_reg[8..12])?; + Ok([ + (cfr_reg[0] as u32) << 24 | (cfr_reg[1] as u32) << 16 | (cfr_reg[2] as u32) << 8 | (cfr_reg[3] as u32), + (cfr_reg[4] as u32) << 24 | (cfr_reg[5] as u32) << 16 | (cfr_reg[6] as u32) << 8 | (cfr_reg[7] as u32), + (cfr_reg[8] as u32) << 24 | (cfr_reg[9] as u32) << 16 | (cfr_reg[10] as u32) << 8 | (cfr_reg[11] as u32) + ]) + } + + /* + * Get a set of configurations using DDSCFRMask + */ + pub fn get_configurations<'w>(&mut self, mask_pairs: &'w mut[(DDSCFRMask, u32)]) -> Result<&'w [(DDSCFRMask, u32)], Error> { + let data_array = self.get_all_configurations()?; + for index in 0..mask_pairs.len() { + mask_pairs[index].1 = match mask_pairs[index].0.get_shift() { + 0..=31 => mask_pairs[index].0.get_filtered_content(data_array[0]), + 32..=63 => mask_pairs[index].0.get_filtered_content(data_array[1]), + 64..=95 => mask_pairs[index].0.get_filtered_content(data_array[2]), + _ => panic!("Invalid DDSCFRMask!"), + } + } + Ok(mask_pairs) + } + + /* + * Write (cfr1, cfr2, cfr3) contents + */ + fn set_all_configurations(&mut self, data_array: [u32; 3]) -> Result<(), Error> { + for register in 0x00..=0x02 { + self.write_register(register, &mut [ + ((data_array[register as usize] >> 24) & 0xFF) as u8, + ((data_array[register as usize] >> 16) & 0xFF) as u8, + ((data_array[register as usize] >> 8 ) & 0xFF) as u8, + ((data_array[register as usize] >> 0 ) & 0xFF) as u8 + ])?; + } + Ok(()) + } + + /* + * Set a set of configurations using DDSCFRMask + */ + pub fn set_configurations(&mut self, mask_pairs: &mut[(DDSCFRMask, u32)]) -> Result<(), Error> { + let mut data_array = self.get_all_configurations()?; + for index in 0..mask_pairs.len() { + // Reject any attempt to rewrite LSB_FIRST and SBIO_INPUT_ONLY + if mask_pairs[index].0 == DDSCFRMask::LSB_FIRST || mask_pairs[index].0 == DDSCFRMask::SDIO_IN_ONLY { + continue; + } + match mask_pairs[index].0.get_shift() { + 0..=31 => mask_pairs[index].0.set_data_by_arg(&mut data_array[0], mask_pairs[index].1), + 32..=63 => mask_pairs[index].0.set_data_by_arg(&mut data_array[1], mask_pairs[index].1), + 64..=95 => mask_pairs[index].0.set_data_by_arg(&mut data_array[2], mask_pairs[index].1), + _ => panic!("Invalid DDSCFRMask!"), + }; + } + self.set_all_configurations(data_array.clone()) + } + + /* + * Setup a complete single tone profile + * Phase: Expressed in positive degree, i.e. [0.0, 360.0) + * Frequency: Must be non-negative + * Amplitude: In a scale from 0 to 1, taking float + */ + pub fn set_single_tone_profile(&mut self, profile: u8, f_out: f64, phase_offset: f64, amp_scale_factor: f64) -> Result<(), Error> { + + assert!(profile < 8); + assert!(f_out >= 0.0); + assert!(phase_offset >= 0.0 && phase_offset < 360.0); + assert!(amp_scale_factor >=0.0 && amp_scale_factor <= 1.0); + + let ftw = self.frequency_to_ftw(f_out); + let pow = self.degree_to_pow(phase_offset); + let asf = self.amplitude_to_asf(amp_scale_factor); + + // Setup configuration registers before writing single tone register + self.enable_single_tone_configuration()?; + + // Transfer single tone profile data + self.write_register(0x0E + profile, &mut [ + ((asf >> 8 ) & 0xFF) as u8, + ((asf >> 0 ) & 0xFF) as u8, + ((pow >> 8 ) & 0xFF) as u8, + ((pow >> 0 ) & 0xFF) as u8, + ((ftw >> 24) & 0xFF) as u8, + ((ftw >> 16) & 0xFF) as u8, + ((ftw >> 8 ) & 0xFF) as u8, + ((ftw >> 0 ) & 0xFF) as u8, + ]) + } + + /* + * Set frequency of a single tone profile + * Frequency: Must be non-negative + * Keep other field unchanged in the register + */ + pub fn set_single_tone_profile_frequency(&mut self, profile: u8, f_out: f64) -> Result<(), Error> { + + // Setup configuration registers before writing single tone register + self.enable_single_tone_configuration()?; + + let ftw = self.frequency_to_ftw(f_out); + + // Read existing amplitude/phase data + let mut register: [u8; 8] = [0; 8]; + self.read_register(0x0E + profile, &mut register)?; + + // Overwrite FTW + register[4] = ((ftw >> 24) & 0xFF) as u8; + register[5] = ((ftw >> 16) & 0xFF) as u8; + register[6] = ((ftw >> 8) & 0xFF) as u8; + register[7] = ((ftw >> 0) & 0xFF) as u8; + + // Update FTW by writing back the register + self.write_register(0x0E + profile, &mut register) + } + + /* + * Set phase offset of a single tone profile + * Phase: Expressed in positive degree, i.e. [0.0, 360.0) + * Keep other field unchanged in the register + */ + pub fn set_single_tone_profile_phase(&mut self, profile: u8, phase_offset: f64) -> Result<(), Error> { + + // Setup configuration registers before writing single tone register + self.enable_single_tone_configuration()?; + + let pow = self.degree_to_pow(phase_offset); + + // Read existing amplitude/frequency data + let mut register: [u8; 8] = [0; 8]; + self.read_register(0x0E + profile, &mut register)?; + + // Overwrite POW + register[2] = ((pow >> 8) & 0xFF) as u8; + register[3] = ((pow >> 0) & 0xFF) as u8; + + // Update POW by writing back the register + self.write_register(0x0E + profile, &mut register) + } + + /* + * Set amplitude offset of a single tone profile + * Amplitude: In a scale from 0 to 1, taking float + * Keep other field unchanged in the register + */ + pub fn set_single_tone_profile_amplitude(&mut self, profile: u8, amp_scale_factor: f64) -> Result<(), Error> { + + // Setup configuration registers before writing single tone register + self.enable_single_tone_configuration()?; + + // Calculate amplitude_scale_factor (ASF) + let asf = self.amplitude_to_asf(amp_scale_factor); + + // Read existing frequency/phase data + let mut register: [u8; 8] = [0; 8]; + self.read_register(0x0E + profile, &mut register)?; + + // Overwrite POW + register[0] = ((asf >> 8) & 0xFF) as u8; + register[1] = ((asf >> 0) & 0xFF) as u8; + + // Update POW by writing back the register + self.write_register(0x0E + profile, &mut register) + } + + // Helper function to switch into single tone mode + // Need to setup configuration registers before writing single tone register + fn enable_single_tone_configuration(&mut self) -> Result<(), Error> { + + self.set_configurations(&mut [ + (DDSCFRMask::RAM_ENABLE, 0), + (DDSCFRMask::DIGITAL_RAMP_ENABLE, 0), + (DDSCFRMask::OSK_ENABLE, 0), + (DDSCFRMask::PARALLEL_DATA_PORT_ENABLE, 0), + ])?; + self.set_configurations(&mut [ + (DDSCFRMask::EN_AMP_SCALE_SINGLE_TONE_PRO, 1), + ]) + } + + // Helper function to switch into RAM mode + // Need to setup configuration registers before writing into RAM profile register + fn enable_ram_configuration(&mut self, ram_dst: RAMDestination) -> Result<(), Error> { + self.set_configurations(&mut [ + (DDSCFRMask::RAM_ENABLE, 1), + (DDSCFRMask::RAM_PLAYBACK_DST, ram_dst as u32), + ]) + } + + // Helper function to switch out of RAM mode + // Need to setup configuration registers before writing into RAM profile register + fn disable_ram_configuration(&mut self) -> Result<(), Error> { + self.set_configurations(&mut [ + (DDSCFRMask::RAM_ENABLE, 0), + ]) + } + + /* + * Configure a RAM mode profile, wrt supplied frequency data + * This will setup the static RAM_VEC by converting frequency to ftw + */ + pub unsafe fn set_frequency_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + frequency_data: &[f64] + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(frequency_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert frequency data into bytes recognized by DDS + for freq in frequency_data.iter() { + let ftw = self.frequency_to_ftw(*freq); + RAM_VEC.push(((ftw >> 24) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((ftw >> 16) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((ftw >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((ftw >> 0) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Frequency, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, wrt supplied amplitude data + * This will setup the static RAM_VEC by converting amplitude to asf + */ + pub unsafe fn set_amplitude_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + amplitude_data: &[f64] + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(amplitude_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert amplitude data into bytes recognized by DDS + for amp in amplitude_data.iter() { + let asf = self.amplitude_to_asf(*amp); + RAM_VEC.push(((asf >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((asf << 2) & 0xFC) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Amplitude, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, wrt supplied phase data + * This will setup the static RAM_VEC by converting phase to ftw + */ + pub unsafe fn set_phase_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + phase_data: &[f64] + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(phase_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert phase data into bytes recognized by DDS + for deg in phase_data.iter() { + let pow = self.degree_to_pow(*deg); + RAM_VEC.push(((pow >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((pow >> 0) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(0) + .map_err(|_| Error::DDSRAMError)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Phase, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, wrt supplied phase data + * This will setup the static RAM_VEC by converting phase to ftw + */ + pub unsafe fn set_polar_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + no_dwell_high: bool, zero_crossing: bool, op_mode: RAMOperationMode, playback_rate: f64, + polar_data: &[(f64, f64)] + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + assert_eq!(polar_data.len() as u16, end_addr - start_addr + 1); + + // Clear RAM vector, and add address byte + RAM_VEC.clear(); + RAM_VEC.push(0x16) + .map_err(|_| Error::DDSRAMError)?; + + // Convert amplitude data into bytes recognized by DDS + for (deg, amp) in polar_data.iter() { + let pow = self.degree_to_pow(*deg); + let asf = self.amplitude_to_asf(*amp); + RAM_VEC.push(((pow >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((pow >> 0) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((asf >> 8) & 0xFF) as u8) + .map_err(|_| Error::DDSRAMError)?; + RAM_VEC.push(((asf << 2) & 0xFC) as u8) + .map_err(|_| Error::DDSRAMError)?; + } + + self.set_ram_profile(profile, start_addr, end_addr, RAMDestination::Phase, + no_dwell_high, zero_crossing, op_mode, playback_rate) + } + + /* + * Configure a RAM mode profile, w.r.t static vector (RAM_VEC) + */ + fn set_ram_profile(&mut self, profile: u8, start_addr: u16, end_addr: u16, + ram_dst: RAMDestination, no_dwell_high: bool, zero_crossing: bool, + op_mode: RAMOperationMode, playback_rate: f64 + ) -> Result<(), Error> { + + // Check the legality of the profile setup + assert!(profile <= 7); + assert!(end_addr >= start_addr); + assert!(end_addr < 1024); + // assert_eq! RAM_VEC.len() as u16, ((end_addr - start_addr + 1) * 4) + 1); + + // Calculate address step rate, and check legality + let step_rate = (self.f_sys_clk/(4.0 * playback_rate)) as u64; + if step_rate == 0 || step_rate > 0xFFFF { + return Err(Error::DDSRAMError); + } + + // Before setting up RAM, disable RAM_ENABLE + self.enable_ram_configuration(ram_dst.clone())?; + + // Write a RAM profile, but include all data in RAM + self.write_register(0x0E + profile, &mut [ + 0x00, + ((step_rate >> 8) & 0xFF).try_into().unwrap(), + ((step_rate >> 0) & 0xFF).try_into().unwrap(), + ((end_addr >> 2) & 0xFF).try_into().unwrap(), + ((end_addr & 0x3) << 6).try_into().unwrap(), + ((start_addr >> 2) & 0xFF).try_into().unwrap(), + ((start_addr & 0x3) << 6).try_into().unwrap(), + ((no_dwell_high as u8) << 5) | ((zero_crossing as u8) << 3) | (op_mode as u8) + ])?; + + // Temporarily disable RAM mode while accessing into RAM + self.disable_ram_configuration()?; + unsafe { + self.write_ram()?; + } + + // Properly configure start_addr and end_addr + self.enable_ram_configuration(ram_dst) + + } + + // Calculate ftw (frequency tuning word) + fn frequency_to_ftw(&mut self, f_out: f64) -> u32 { + let f_res: u64 = 1 << 32; + ((f_res as f64) * f_out / self.f_sys_clk) as u32 + } + + // Calculate pow (Phase Offset Word) + fn degree_to_pow(&mut self, phase_offset: f64) -> u16 { + // Calculate phase offset word (POW) + let phase_res: u64 = 1 << 16; + ((phase_res as f64) * phase_offset / 360.0) as u16 + } + + // Calculate asf (Amplitude Scale Factor) + fn amplitude_to_asf(&mut self, amplitude: f64) -> u16 { + let amp_res: u64 = 0x3FFF; + ((amp_res as f64) * amplitude) as u16 + } + + // Write data in RAM + unsafe fn write_ram(&mut self) -> Result<(), Error> { + self.spi.transfer(&mut RAM_VEC) + .map(|_| ()) + .map_err(Error::SPI) + } + + /* + * Test method for DDS. + * Return the number of test failed. + */ + pub fn test(&mut self) -> Result> { + // Test configuration register by getting SDIO_IN_ONLY and LSB_FIRST. + let mut error_count = 0; + let mut config_checks = [ + (DDSCFRMask::SDIO_IN_ONLY, 1), + (DDSCFRMask::LSB_FIRST, 0) + ]; + self.get_configurations(&mut config_checks)?; + if config_checks[0].1 == 0 { + error_count += 1; + } + if config_checks[1].1 == 1 { + error_count += 1; + } + Ok(error_count) + } + + // Setter function for f_sys_clk + // Warning: This does not setup the chip to generate this actual f_sys_clk + pub(crate) fn set_f_sys_clk(&mut self, f_sys_clk: f64) { + self.f_sys_clk = f_sys_clk; + } + + // Getter function for f_sys_clk + pub fn get_f_sys_clk(&mut self) -> f64 { + self.f_sys_clk + } } // Strong check for bytes passed to a register macro_rules! impl_register_io { - ($($reg_addr: expr, $reg_byte_size: expr),+) => { - impl DDS - where - SPI: Transfer - { - pub fn write_register(&mut self, addr: u8, bytes: &mut[u8]) -> Result<(), Error> { - match addr { - $( - $reg_addr => { - assert_eq!(bytes.len(), $reg_byte_size); - let mut arr: [u8; $reg_byte_size + 1] = [0; ($reg_byte_size + 1)]; - arr[0] = addr | WRITE_MASK; - for i in 0..$reg_byte_size { - arr[i+1] = bytes[i]; - } - self.spi.transfer(&mut arr) - .map(|_| ()) - .map_err(Error::SPI) - }, - )* - _ => panic!("Bad address for DDS writing.") - } - } + ($($reg_addr: expr, $reg_byte_size: expr),+) => { + impl DDS + where + SPI: Transfer + { + pub fn write_register(&mut self, addr: u8, bytes: &mut[u8]) -> Result<(), Error> { + match addr { + $( + $reg_addr => { + assert_eq!(bytes.len(), $reg_byte_size); + let mut arr: [u8; $reg_byte_size + 1] = [0; ($reg_byte_size + 1)]; + arr[0] = addr | WRITE_MASK; + for i in 0..$reg_byte_size { + arr[i+1] = bytes[i]; + } + self.spi.transfer(&mut arr) + .map(|_| ()) + .map_err(Error::SPI) + }, + )* + _ => panic!("Bad address for DDS writing.") + } + } - pub fn read_register<'w>(&mut self, addr: u8, bytes: &'w mut[u8]) -> Result<&'w [u8], Error> { - match addr { - $( - $reg_addr => { - assert_eq!(bytes.len(), $reg_byte_size); - let mut arr: [u8; $reg_byte_size + 1] = [0; ($reg_byte_size + 1)]; - arr[0] = addr | READ_MASK; - match self.spi.transfer(&mut arr).map_err(Error::SPI) { - Ok(ret) => { - assert_eq!(ret.len(), $reg_byte_size + 1); - for i in 0..$reg_byte_size { - bytes[i] = ret[i+1]; - } - Ok(bytes) - }, - Err(e) => Err(e), - } - }, - )* - _ => panic!("Bad address for DDS reading.") - } - } - } - } + pub fn read_register<'w>(&mut self, addr: u8, bytes: &'w mut[u8]) -> Result<&'w [u8], Error> { + match addr { + $( + $reg_addr => { + assert_eq!(bytes.len(), $reg_byte_size); + let mut arr: [u8; $reg_byte_size + 1] = [0; ($reg_byte_size + 1)]; + arr[0] = addr | READ_MASK; + match self.spi.transfer(&mut arr).map_err(Error::SPI) { + Ok(ret) => { + assert_eq!(ret.len(), $reg_byte_size + 1); + for i in 0..$reg_byte_size { + bytes[i] = ret[i+1]; + } + Ok(bytes) + }, + Err(e) => Err(e), + } + }, + )* + _ => panic!("Bad address for DDS reading.") + } + } + } + } } impl_register_io!( - 0x00, 4, - 0x01, 4, - 0x02, 4, - 0x03, 4, - 0x04, 4, - 0x07, 4, - 0x08, 2, - 0x09, 4, - 0x0A, 4, - 0x0B, 8, - 0x0C, 8, - 0x0D, 4, - 0x0E, 8, - 0x0F, 8, - 0x10, 8, - 0x11, 8, - 0x12, 8, - 0x13, 8, - 0x14, 8, - 0x15, 8 + 0x00, 4, + 0x01, 4, + 0x02, 4, + 0x03, 4, + 0x04, 4, + 0x07, 4, + 0x08, 2, + 0x09, 4, + 0x0A, 4, + 0x0B, 8, + 0x0C, 8, + 0x0D, 4, + 0x0E, 8, + 0x0F, 8, + 0x10, 8, + 0x11, 8, + 0x12, 8, + 0x13, 8, + 0x14, 8, + 0x15, 8 ); diff --git a/src/flash.rs b/src/flash.rs index c577a3a..374f371 100644 --- a/src/flash.rs +++ b/src/flash.rs @@ -1,5 +1,5 @@ use embedded_hal::{ - digital::v2::{OutputPin, InputPin}, + digital::v2::{OutputPin, InputPin}, blocking::spi::Transfer, blocking::delay::DelayUs, }; @@ -21,65 +21,65 @@ pub fn flash_ice40_fpga, DONE: InputPin> (mut spi: SPI, mut ss: SS, mut creset: RST, cdone: DONE, mut delay: DELAY) -> Result<(), FPGAFlashError> { - // Data buffer setup - let mut dummy_byte :[u8; 1] = [0x00]; - let mut dummy_13_bytes :[u8; 13] = [0x00; 13]; + // Data buffer setup + let mut dummy_byte :[u8; 1] = [0x00]; + let mut dummy_13_bytes :[u8; 13] = [0x00; 13]; - // Drive CRESET_B low + // Drive CRESET_B low creset.set_low() .map_err(|_| FPGAFlashError::NegotiationError)?; - // Drive SPI_SS_B low + // Drive SPI_SS_B low ss.set_low() .map_err(|_| FPGAFlashError::NegotiationError)?; - // Wait at least 200ns - delay.delay_us(1_u32); + // Wait at least 200ns + delay.delay_us(1_u32); - // Drive CRESET_B high + // Drive CRESET_B high creset.set_high() .map_err(|_| FPGAFlashError::NegotiationError)?; - // Wait at least another 1200us to clear internal config memory - delay.delay_us(1200_u32); + // Wait at least another 1200us to clear internal config memory + delay.delay_us(1200_u32); // Before data transmission starts, check if C_DONE is truly low // If C_DONE is high, the FPGA reset procedure is unsuccessful - match cdone.is_low() { + match cdone.is_low() { Ok(true) => {}, _ => return Err(FPGAFlashError::ResetStatusError), }; - // Set SPI_SS_B high + // Set SPI_SS_B high ss.set_high() .map_err(|_| FPGAFlashError::NegotiationError)?; - // Send 8 dummy clock, effectively 1 byte of 0x00 + // Send 8 dummy clock, effectively 1 byte of 0x00 spi.transfer(&mut dummy_byte) .map_err(|_| FPGAFlashError::SPICommunicationError)?; - // Drive SPI_SS_B low + // Drive SPI_SS_B low ss.set_low() .map_err(|_| FPGAFlashError::NegotiationError)?; - // Send the whole image without interruption - for byte in DATA.into_iter() { + // Send the whole image without interruption + for byte in DATA.into_iter() { let mut single_byte_slice = [*byte]; spi.transfer(&mut single_byte_slice) .map_err(|_| FPGAFlashError::SPICommunicationError)?; - } + } - // Drive SPI_SS_B high + // Drive SPI_SS_B high ss.set_high() .map_err(|_| FPGAFlashError::NegotiationError)?; - // Send at another 100 dummy clocks (choosing 13 bytes) + // Send at another 100 dummy clocks (choosing 13 bytes) spi.transfer(&mut dummy_13_bytes) .map_err(|_| FPGAFlashError::SPICommunicationError)?; // Check the CDONE output from FPGA // CDONE needs to be high - match cdone.is_high() { + match cdone.is_high() { Ok(true) => {}, _ => return Err(FPGAFlashError::ResetStatusError), }; diff --git a/src/main.rs b/src/main.rs index 7ba7662..dd4e34a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,16 @@ #![no_main] #![no_std] #![feature(str_strip)] -use log::{ trace, debug, info, warn }; -use stm32h7xx_hal::hal::digital::v2::InputPin; +#![feature(core_intrinsics)] + +use log::{ trace }; use stm32h7xx_hal::gpio::Speed; use stm32h7xx_hal::{pac, prelude::*, spi}; use stm32h7xx_hal::ethernet; use smoltcp as net; use minimq::{ - embedded_nal::{IpAddr, Ipv4Addr, TcpStack}, + embedded_nal::{ IpAddr, Ipv4Addr }, MqttClient, QoS }; @@ -17,7 +18,6 @@ use cortex_m; use cortex_m_rt::entry; use rtic::cyccnt::{Instant, U32Ext}; -use heapless::Vec; use heapless::consts; #[macro_use] @@ -70,31 +70,31 @@ macro_rules! add_socket { #[entry] fn main() -> ! { - let mut cp = cortex_m::Peripherals::take().unwrap(); - let dp = pac::Peripherals::take().unwrap(); + let mut cp = cortex_m::Peripherals::take().unwrap(); + let dp = pac::Peripherals::take().unwrap(); - unsafe { - logger::enable_itm(&dp.DBGMCU, &mut cp.DCB, &mut cp.ITM); - } - logger::init(); + unsafe { + logger::enable_itm(&dp.DBGMCU, &mut cp.DCB, &mut cp.ITM); + } + logger::init(); // Enable SRAM3 for the descriptor ring. dp.RCC.ahb2enr.modify(|_, w| w.sram3en().set_bit()); // // Reset RCC clock - // dp.RCC.rsr.write(|w| w.rmvf().set_bit()); + // dp.RCC.rsr.write(|w| w.rmvf().set_bit()); - let pwr = dp.PWR.constrain(); - let vos = pwr.freeze(); + let pwr = dp.PWR.constrain(); + let vos = pwr.freeze(); - let rcc = dp.RCC.constrain(); - let ccdr = rcc - .use_hse(8.mhz()) - .sys_ck(400.mhz()) - .hclk(200.mhz()) - .pll1_q_ck(48.mhz()) - .pll1_r_ck(400.mhz()) - .freeze(vos, &dp.SYSCFG); - + let rcc = dp.RCC.constrain(); + let ccdr = rcc + .use_hse(8.mhz()) + .sys_ck(400.mhz()) + .hclk(200.mhz()) + .pll1_q_ck(48.mhz()) + .pll1_r_ck(400.mhz()) + .freeze(vos, &dp.SYSCFG); + let delay = cp.SYST.delay(ccdr.clocks); cp.SCB.invalidate_icache(); @@ -102,42 +102,42 @@ fn main() -> ! { cp.DWT.enable_cycle_counter(); - let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); - let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); - let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); - let gpiod = dp.GPIOD.split(ccdr.peripheral.GPIOD); - let gpioe = dp.GPIOE.split(ccdr.peripheral.GPIOE); - let gpiof = dp.GPIOF.split(ccdr.peripheral.GPIOF); + let gpioa = dp.GPIOA.split(ccdr.peripheral.GPIOA); + let gpiob = dp.GPIOB.split(ccdr.peripheral.GPIOB); + let gpioc = dp.GPIOC.split(ccdr.peripheral.GPIOC); + let gpiod = dp.GPIOD.split(ccdr.peripheral.GPIOD); + let _gpioe = dp.GPIOE.split(ccdr.peripheral.GPIOE); + let gpiof = dp.GPIOF.split(ccdr.peripheral.GPIOF); let gpiog = dp.GPIOG.split(ccdr.peripheral.GPIOG); // Note: ITM doesn't work beyond this, due to a pin conflict between: // - FPGA_SPI: SCK (af5) // - ST_LINK SWO (af0) // Both demands PB3 - trace!("Flashing configuration bitstream to iCE40 HX8K on Humpback."); + trace!("Flashing configuration bitstream to iCE40 HX8K on Humpback."); - // Using SPI_1 alternate functions (af5) - let fpga_sck = gpiob.pb3.into_alternate_af5(); - let fpga_sdo = gpiob.pb4.into_alternate_af5(); - let fpga_sdi = gpiob.pb5.into_alternate_af5(); + // Using SPI_1 alternate functions (af5) + let fpga_sck = gpiob.pb3.into_alternate_af5(); + let fpga_sdo = gpiob.pb4.into_alternate_af5(); + let fpga_sdi = gpiob.pb5.into_alternate_af5(); - // Setup SPI_SS_B and CRESET_B - let fpga_ss = gpioa.pa4.into_push_pull_output(); - let fpga_creset = gpiof.pf3.into_open_drain_output(); + // Setup SPI_SS_B and CRESET_B + let fpga_ss = gpioa.pa4.into_push_pull_output(); + let fpga_creset = gpiof.pf3.into_open_drain_output(); - // Setup CDONE - let fpga_cdone = gpiod.pd15.into_pull_up_input(); + // Setup CDONE + let fpga_cdone = gpiod.pd15.into_pull_up_input(); - // Setup SPI interface - let fpga_cfg_spi = dp.SPI1.spi( - (fpga_sck, fpga_sdo, fpga_sdi), - spi::MODE_3, - 12.mhz(), - ccdr.peripheral.SPI1, - &ccdr.clocks, - ); + // Setup SPI interface + let fpga_cfg_spi = dp.SPI1.spi( + (fpga_sck, fpga_sdo, fpga_sdi), + spi::MODE_3, + 12.mhz(), + ccdr.peripheral.SPI1, + &ccdr.clocks, + ); - flash_ice40_fpga(fpga_cfg_spi, fpga_ss, fpga_creset, fpga_cdone, delay).unwrap(); + flash_ice40_fpga(fpga_cfg_spi, fpga_ss, fpga_creset, fpga_cdone, delay).unwrap(); // Configure ethernet IO { @@ -183,46 +183,46 @@ fn main() -> ! { .routes(routes) .finalize(); - /* - * Using SPI6 - * SCLK -> PA5 (af8) - * MOSI -> PG14 (af5) - * MISO -> PA6 (af8) - * CS -> 0: PB12, 1: PA15, 2: PC7 - */ - let sclk = gpioa.pa5.into_alternate_af8().set_speed(Speed::VeryHigh); - let mosi = gpiog.pg14.into_alternate_af5().set_speed(Speed::VeryHigh); - let miso = gpioa.pa6.into_alternate_af8().set_speed(Speed::VeryHigh); - let (cs0, cs1, cs2) = ( - gpiob.pb12.into_push_pull_output(), - gpioa.pa15.into_push_pull_output(), - gpioc.pc7.into_push_pull_output(), - ); + /* + * Using SPI6 + * SCLK -> PA5 (af8) + * MOSI -> PG14 (af5) + * MISO -> PA6 (af8) + * CS -> 0: PB12, 1: PA15, 2: PC7 + */ + let sclk = gpioa.pa5.into_alternate_af8().set_speed(Speed::VeryHigh); + let mosi = gpiog.pg14.into_alternate_af5().set_speed(Speed::VeryHigh); + let miso = gpioa.pa6.into_alternate_af8().set_speed(Speed::VeryHigh); + let (cs0, cs1, cs2) = ( + gpiob.pb12.into_push_pull_output(), + gpioa.pa15.into_push_pull_output(), + gpioc.pc7.into_push_pull_output(), + ); - /* - * I/O_Update -> PB15 - */ - let io_update = gpiob.pb15.into_push_pull_output(); + /* + * I/O_Update -> PB15 + */ + let io_update = gpiob.pb15.into_push_pull_output(); - let spi = dp.SPI6.spi( - (sclk, miso, mosi), - spi::MODE_0, - 10.mhz(), - ccdr.peripheral.SPI6, - &ccdr.clocks, - ); + let spi = dp.SPI6.spi( + (sclk, miso, mosi), + spi::MODE_0, + 2.mhz(), + ccdr.peripheral.SPI6, + &ccdr.clocks, + ); - let switch = CPLD::new(spi, (cs0, cs1, cs2), io_update); - let parts = switch.split(); + let switch = CPLD::new(spi, (cs0, cs1, cs2), io_update); + let parts = switch.split(); let mut urukul = Urukul::new( parts.spi1, parts.spi2, parts.spi3, parts.spi4, parts.spi5, parts.spi6, parts.spi7 ); - urukul.reset().unwrap(); - // info!("Test value: {}", urukul.test().unwrap()); + urukul.reset().unwrap(); + // info!("Test value: {}", urukul.test().unwrap()); - let mut mqtt_mux = MqttMux::new(urukul); + let mut mqtt_mux = MqttMux::new(urukul); // Time unit in ms let mut time: u32 = 0; @@ -271,7 +271,7 @@ fn main() -> ! { .poll(|_client, topic, message, _properties| { // info!("On {:?}, received: {:?}", topic, message); // Why is topic a string while message is a slice? - mqtt_mux.process_mqtt(topic, message).is_ok(); + mqtt_mux.process_mqtt(topic, message).unwrap(); }).is_ok(); if connection && !has_subscribed && tick { diff --git a/src/spi_slave.rs b/src/spi_slave.rs index 03d5627..8859b08 100644 --- a/src/spi_slave.rs +++ b/src/spi_slave.rs @@ -1,61 +1,61 @@ use embedded_hal::{ - blocking::spi::Transfer, - digital::v2::OutputPin, + blocking::spi::Transfer, + digital::v2::OutputPin, }; use crate::cpld::CPLD; use crate::urukul::Error; pub struct SPISlave<'a, SPI, CS0, CS1, CS2, GPIO> ( - // SPI device to be multiplexed - &'a CPLD, - // Channel of SPI slave - u8, - // Need I/O Update - bool, + // SPI device to be multiplexed + &'a CPLD, + // Channel of SPI slave + u8, + // Need I/O Update + bool, ); pub struct Parts<'a, SPI, CS0, CS1, CS2, GPIO> { - pub spi1: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, - pub spi2: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, - pub spi3: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, - pub spi4: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, - pub spi5: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, - pub spi6: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, - pub spi7: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, + pub spi1: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, + pub spi2: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, + pub spi3: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, + pub spi4: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, + pub spi5: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, + pub spi6: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, + pub spi7: SPISlave<'a, SPI, CS0, CS1, CS2, GPIO>, } impl<'a, SPI, CS0, CS1, CS2, GPIO> Parts<'a, SPI, CS0, CS1, CS2, GPIO> { - pub(crate) fn new(cpld: &'a CPLD) -> Self { - Parts { - spi1: SPISlave(&cpld, 1, false), - spi2: SPISlave(&cpld, 2, false), - spi3: SPISlave(&cpld, 3, false), - spi4: SPISlave(&cpld, 4, true), - spi5: SPISlave(&cpld, 5, true), - spi6: SPISlave(&cpld, 6, true), - spi7: SPISlave(&cpld, 7, true), - } - } + pub(crate) fn new(cpld: &'a CPLD) -> Self { + Parts { + spi1: SPISlave(&cpld, 1, false), + spi2: SPISlave(&cpld, 2, false), + spi3: SPISlave(&cpld, 3, false), + spi4: SPISlave(&cpld, 4, true), + spi5: SPISlave(&cpld, 5, true), + spi6: SPISlave(&cpld, 6, true), + spi7: SPISlave(&cpld, 7, true), + } + } } impl<'a, SPI, CS0, CS1, CS2, GPIO, E> Transfer for SPISlave<'a, SPI, CS0, CS1, CS2, GPIO> where - CS2: OutputPin, - CS1: OutputPin, - CS0: OutputPin, - SPI: Transfer, - GPIO: OutputPin, + CS2: OutputPin, + CS1: OutputPin, + CS0: OutputPin, + SPI: Transfer, + GPIO: OutputPin, { - type Error = Error; + type Error = Error; - fn transfer<'w>(&mut self, words: &'w mut[u8]) -> Result<&'w [u8], Self::Error> { - let mut dev = self.0.data.try_borrow_mut().map_err(|_| Error::GetRefMutDataError)?; - dev.select_chip(self.1).map_err(|_| Error::CSError)?; - let result = dev.spi.transfer(words).map_err(Error::SPI)?; - dev.select_chip(0).map_err(|_| Error::CSError)?; - if self.2 { - dev.issue_io_update().map_err(|_| Error::IOUpdateError)?; - } - Ok(result) - } + fn transfer<'w>(&mut self, words: &'w mut[u8]) -> Result<&'w [u8], Self::Error> { + let mut dev = self.0.data.try_borrow_mut().map_err(|_| Error::GetRefMutDataError)?; + dev.select_chip(self.1).map_err(|_| Error::CSError)?; + let result = dev.spi.transfer(words).map_err(Error::SPI)?; + dev.select_chip(0).map_err(|_| Error::CSError)?; + if self.2 { + dev.issue_io_update().map_err(|_| Error::IOUpdateError)?; + } + Ok(result) + } } diff --git a/src/urukul.rs b/src/urukul.rs index a876ebe..4464ab8 100644 --- a/src/urukul.rs +++ b/src/urukul.rs @@ -1,6 +1,6 @@ extern crate embedded_hal; use embedded_hal::{ - blocking::spi::Transfer, + blocking::spi::Transfer, }; use crate::config_register::ConfigRegister; @@ -10,329 +10,328 @@ use crate::attenuator::Attenuator; use crate::dds::DDS; /* - * Enum for structuring error + * Enum for structuring error */ #[derive(Debug)] pub enum Error { - SPI(E), - CSError, - GetRefMutDataError, - AttenuatorError, - IOUpdateError, - DDSError, - ConfigRegisterError, - DDSCLKError, - DDSRAMError, - ParameterError, - MqttTopicError, - MqttCommandError, + SPI(E), + CSError, + GetRefMutDataError, + AttenuatorError, + IOUpdateError, + DDSError, + ConfigRegisterError, + DDSCLKError, + DDSRAMError, + ParameterError, + MqttTopicError, + MqttCommandError, } #[derive(Debug, Clone)] pub enum ClockSource { - OSC, - SMA, - MMCX, + OSC, + SMA, + MMCX, } /* * Struct for Urukul master device */ pub struct Urukul { - config_register: ConfigRegister, - attenuator: Attenuator, - multi_dds: DDS, - dds: [DDS; 4], - f_master_clk: f64, + config_register: ConfigRegister, + attenuator: Attenuator, + multi_dds: DDS, + dds: [DDS; 4], + f_master_clk: f64, } impl Urukul where - SPI: Transfer, + SPI: Transfer, { - /* - * Master constructor for the entire Urukul device - * Supply 7 SPI channels to Urukul and 4 reference clock frequencies - */ - pub fn new(spi1: SPI, spi2: SPI, spi3: SPI, spi4: SPI, spi5: SPI, spi6: SPI, spi7: SPI) -> Self { - // Construct Urukul - Urukul { - config_register: ConfigRegister::new(spi1), - attenuator: Attenuator::new(spi2), - // Create a multi-channel DDS with predefined 25MHz clock - multi_dds: DDS::new(spi3, 25_000_000.0), - // Create 4 DDS instances with predefined 25MHz clock - // Counter-intuitive to assign urukul clock before having a urukul - dds: [ - DDS::new(spi4, 25_000_000.0), - DDS::new(spi5, 25_000_000.0), - DDS::new(spi6, 25_000_000.0), - DDS::new(spi7, 25_000_000.0), - ], - // Default clock selection: OSC, predefined 100MHz speed - f_master_clk: 100_000_000.0, - } - } + /* + * Master constructor for the entire Urukul device + * Supply 7 SPI channels to Urukul and 4 reference clock frequencies + */ + pub fn new(spi1: SPI, spi2: SPI, spi3: SPI, spi4: SPI, spi5: SPI, spi6: SPI, spi7: SPI) -> Self { + // Construct Urukul + Urukul { + config_register: ConfigRegister::new(spi1), + attenuator: Attenuator::new(spi2), + // Create a multi-channel DDS with predefined 25MHz clock + multi_dds: DDS::new(spi3, 25_000_000.0), + // Create 4 DDS instances with predefined 25MHz clock + // Counter-intuitive to assign urukul clock before having a urukul + dds: [ + DDS::new(spi4, 25_000_000.0), + DDS::new(spi5, 25_000_000.0), + DDS::new(spi6, 25_000_000.0), + DDS::new(spi7, 25_000_000.0), + ], + // Default clock selection: OSC, predefined 100MHz speed + f_master_clk: 100_000_000.0, + } + } - /* - * Reset method. To be invoked by initialization and manual reset. - * Only Urukul struct provides reset method. - * DDS reset is controlled by Urukul (RST). - * Attenuators only have shift register reset, which does not affect its data - * CPLD only has a "all-zero" default state. - */ - pub fn reset(&mut self) -> Result<(), Error> { - // Reset DDS and attenuators - self.config_register.set_configurations(&mut [ - (CFGMask::RST, 1), - (CFGMask::IO_RST, 1), - (CFGMask::IO_UPDATE, 0) - ])?; - // Set 0 to all fields on configuration register. - self.config_register.set_configurations(&mut [ - (CFGMask::RF_SW, 0), - (CFGMask::LED, 0), - (CFGMask::PROFILE, 0), - (CFGMask::IO_UPDATE, 0), - (CFGMask::MASK_NU, 0), - (CFGMask::CLK_SEL0, 0), - (CFGMask::SYNC_SEL, 0), - (CFGMask::RST, 0), - (CFGMask::IO_RST, 0), - (CFGMask::CLK_SEL1, 0), - (CFGMask::DIV, 0), - ])?; - // Init all DDS chips. Configure SDIO as input only. - for chip_no in 0..4 { - self.dds[chip_no].init()?; - } - // Clock tree reset. OSC clock source by default - self.f_master_clk = 100_000_000.0; - // CPLD divides clock frequency by 4 by default. - for chip_no in 0..4 { - self.dds[chip_no].set_ref_clk_frequency(self.f_master_clk / 4.0)?; - } - Ok(()) - } + /* + * Reset method. To be invoked by initialization and manual reset. + * Only Urukul struct provides reset method. + * DDS reset is controlled by Urukul (RST). + * Attenuators only have shift register reset, which does not affect its data + * CPLD only has a "all-zero" default state. + */ + pub fn reset(&mut self) -> Result<(), Error> { + // Reset DDS and attenuators + self.config_register.set_configurations(&mut [ + (CFGMask::RST, 1), + (CFGMask::IO_RST, 1), + (CFGMask::IO_UPDATE, 0) + ])?; + // Set 0 to all fields on configuration register. + self.config_register.set_configurations(&mut [ + (CFGMask::RF_SW, 0), + (CFGMask::LED, 0), + (CFGMask::PROFILE, 0), + (CFGMask::IO_UPDATE, 0), + (CFGMask::MASK_NU, 0), + (CFGMask::CLK_SEL0, 0), + (CFGMask::SYNC_SEL, 0), + (CFGMask::RST, 0), + (CFGMask::IO_RST, 0), + (CFGMask::CLK_SEL1, 0), + (CFGMask::DIV, 0), + ])?; + // Init all DDS chips. Configure SDIO as input only. + for chip_no in 0..4 { + self.dds[chip_no].init()?; + } + // Clock tree reset. OSC clock source by default + self.f_master_clk = 100_000_000.0; + // CPLD divides clock frequency by 4 by default. + for chip_no in 0..4 { + self.dds[chip_no].set_ref_clk_frequency(self.f_master_clk / 4.0)?; + } + Ok(()) + } - /* - * Test method fo Urukul. - * Return the number of test failed. - */ - pub fn test(&mut self) -> Result> { - let mut count = self.config_register.test()?; - count += self.attenuator.test()?; - for chip_no in 0..4 { - count += self.dds[chip_no].test()?; - } - Ok(count) - } + /* + * Test method fo Urukul. + * Return the number of test failed. + */ + pub fn test(&mut self) -> Result> { + let mut count = self.config_register.test()?; + count += self.attenuator.test()?; + for chip_no in 0..4 { + count += self.dds[chip_no].test()?; + } + Ok(count) + } } impl Urukul where - SPI: Transfer + SPI: Transfer { - pub fn get_channel_switch_status(&mut self, channel: u32) -> Result> { - if channel < 4 { - self.config_register.get_status(StatusMask::RF_SW).map(|val| (val & (1 << channel)) != 0) - } else { - Err(Error::ParameterError) - } - } + pub fn get_channel_switch_status(&mut self, channel: u32) -> Result> { + if channel < 4 { + self.config_register.get_status(StatusMask::RF_SW).map(|val| (val & (1 << channel)) != 0) + } else { + Err(Error::ParameterError) + } + } - pub fn set_channel_switch(&mut self, channel: u32, status: bool) -> Result<(), Error> { - if channel < 4 { - let prev = u32::from(self.config_register.get_status(StatusMask::RF_SW)?); - let next = { - if status { - prev | (1 << channel) - } else { - prev & (!(1 << channel)) - } - }; - self.config_register.set_configurations(&mut [ - (CFGMask::RF_SW, next), - ]).map(|_| ()) - } else { - Err(Error::ParameterError) - } - } + pub fn set_channel_switch(&mut self, channel: u32, status: bool) -> Result<(), Error> { + if channel < 4 { + let prev = u32::from(self.config_register.get_status(StatusMask::RF_SW)?); + let next = { + if status { + prev | (1 << channel) + } else { + prev & (!(1 << channel)) + } + }; + self.config_register.set_configurations(&mut [ + (CFGMask::RF_SW, next), + ]).map(|_| ()) + } else { + Err(Error::ParameterError) + } + } - pub fn set_clock(&mut self, source: ClockSource, frequency: f64, division: u8) -> Result<(), Error> { - // Change clock source through configuration register - self.set_clock_source(source)?; + pub fn set_clock(&mut self, source: ClockSource, frequency: f64, division: u8) -> Result<(), Error> { + // Change clock source through configuration register + self.set_clock_source(source)?; - // Modify the master clock frequency - // Prevent redundunt call to change f_ref_clk - self.f_master_clk = frequency; + // Modify the master clock frequency + // Prevent redundunt call to change f_ref_clk + self.f_master_clk = frequency; - self.set_clock_division(division) - } + self.set_clock_division(division) + } - pub fn set_clock_source(&mut self, source: ClockSource) -> Result<(), Error> { - // Change clock source through configuration register - match source { - ClockSource::OSC => self.config_register.set_configurations(&mut [ - (CFGMask::CLK_SEL0, 0), - (CFGMask::CLK_SEL1, 0), - ]), - ClockSource::MMCX => self.config_register.set_configurations(&mut [ - (CFGMask::CLK_SEL0, 0), - (CFGMask::CLK_SEL1, 1), - ]), - ClockSource::SMA => self.config_register.set_configurations(&mut [ - (CFGMask::CLK_SEL0, 1), - ]), - }.map(|_| ()) - } + pub fn set_clock_source(&mut self, source: ClockSource) -> Result<(), Error> { + // Change clock source through configuration register + match source { + ClockSource::OSC => self.config_register.set_configurations(&mut [ + (CFGMask::CLK_SEL0, 0), + (CFGMask::CLK_SEL1, 0), + ]), + ClockSource::MMCX => self.config_register.set_configurations(&mut [ + (CFGMask::CLK_SEL0, 0), + (CFGMask::CLK_SEL1, 1), + ]), + ClockSource::SMA => self.config_register.set_configurations(&mut [ + (CFGMask::CLK_SEL0, 1), + ]), + }.map(|_| ()) + } - pub fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), Error> { - // Update master clock frequency - self.f_master_clk = frequency; + pub fn set_clock_frequency(&mut self, frequency: f64) -> Result<(), Error> { + // Update master clock frequency + self.f_master_clk = frequency; - // Update all DDS f_ref_clk - self.set_dds_ref_clk() - } + // Update all DDS f_ref_clk + self.set_dds_ref_clk() + } - pub fn set_clock_division(&mut self, division: u8) -> Result<(), Error> { - match division { - 1 => self.config_register.set_configurations(&mut [ - (CFGMask::DIV, 1), - ]), - 2 => self.config_register.set_configurations(&mut [ - (CFGMask::DIV, 2), - ]), - 4 => self.config_register.set_configurations(&mut [ - (CFGMask::DIV, 3), - ]), - _ => Err(Error::ParameterError), - }?; + pub fn set_clock_division(&mut self, division: u8) -> Result<(), Error> { + match division { + 1 => self.config_register.set_configurations(&mut [ + (CFGMask::DIV, 1), + ]), + 2 => self.config_register.set_configurations(&mut [ + (CFGMask::DIV, 2), + ]), + 4 => self.config_register.set_configurations(&mut [ + (CFGMask::DIV, 3), + ]), + _ => Err(Error::ParameterError), + }?; - self.set_dds_ref_clk() - } + self.set_dds_ref_clk() + } - fn set_dds_ref_clk(&mut self) -> Result<(), Error> { - // Calculate reference clock frequency after clock division from configuration register - let f_ref_clk = self.f_master_clk / (self.get_master_clock_division() as f64); + fn set_dds_ref_clk(&mut self) -> Result<(), Error> { + // Calculate reference clock frequency after clock division from configuration register + let f_ref_clk = self.f_master_clk / (self.get_master_clock_division() as f64); - // Update all DDS chips on reference clock frequency - for dds_channel in 0..4 { - self.dds[dds_channel].set_ref_clk_frequency(f_ref_clk)?; - } - Ok(()) - } + // Update all DDS chips on reference clock frequency + for dds_channel in 0..4 { + self.dds[dds_channel].set_ref_clk_frequency(f_ref_clk)?; + } + Ok(()) + } - fn get_master_clock_division(&mut self) -> u8 { - match self.config_register.get_configuration(CFGMask::DIV) { - 0 | 3 => 4, - 1 => 1, - 2 => 2, - _ => panic!("Divisor out of range, when reading configuration register (CPLD)."), - } - } + fn get_master_clock_division(&mut self) -> u8 { + match self.config_register.get_configuration(CFGMask::DIV) { + 0 | 3 => 4, + 1 => 1, + 2 => 2, + _ => panic!("Divisor out of range, when reading configuration register (CPLD)."), + } + } - pub fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Error> { - if channel >= 4 || attenuation < 0.0 || attenuation > 31.5 { - return Err(Error::ParameterError); - } - self.attenuator.set_channel_attenuation(channel, attenuation) - } + pub fn set_channel_attenuation(&mut self, channel: u8, attenuation: f32) -> Result<(), Error> { + if channel >= 4 || attenuation < 0.0 || attenuation > 31.5 { + return Err(Error::ParameterError); + } + self.attenuator.set_channel_attenuation(channel, attenuation) + } - pub fn set_profile(&mut self, profile: u8) -> Result<(), Error> { - if profile >= 8 { - return Err(Error::ParameterError); - } - self.config_register.set_configurations(&mut [ - (CFGMask::PROFILE, profile.into()) - ]).map(|_| ()) - } + pub fn set_profile(&mut self, profile: u8) -> Result<(), Error> { + if profile >= 8 { + return Err(Error::ParameterError); + } + self.config_register.set_configurations(&mut [ + (CFGMask::PROFILE, profile.into()) + ]).map(|_| ()) + } - pub fn set_channel_single_tone_profile(&mut self, channel: u8, profile: u8, frequency: f64, phase: f64, amplitude: f64) -> Result<(), Error> { - if channel >= 4 || profile >= 8 || frequency < 0.0 || phase >= 360.0 || - phase < 0.0 || amplitude < 0.0 || amplitude > 1.0 { - return Err(Error::ParameterError); - } - self.dds[usize::from(channel)].set_single_tone_profile(profile, frequency, phase, amplitude) - } + pub fn set_channel_single_tone_profile(&mut self, channel: u8, profile: u8, frequency: f64, phase: f64, amplitude: f64) -> Result<(), Error> { + if channel >= 4 || profile >= 8 || frequency < 0.0 || phase >= 360.0 || + phase < 0.0 || amplitude < 0.0 || amplitude > 1.0 { + return Err(Error::ParameterError); + } + self.dds[usize::from(channel)].set_single_tone_profile(profile, frequency, phase, amplitude) + } - pub fn set_channel_single_tone_profile_frequency(&mut self, channel: u8, profile: u8, frequency: f64)-> Result<(), Error> { - if channel >= 4 || profile >= 8 || frequency < 0.0 { - return Err(Error::ParameterError); - } - self.dds[usize::from(channel)].set_single_tone_profile_frequency(profile, frequency) - } + pub fn set_channel_single_tone_profile_frequency(&mut self, channel: u8, profile: u8, frequency: f64)-> Result<(), Error> { + if channel >= 4 || profile >= 8 || frequency < 0.0 { + return Err(Error::ParameterError); + } + self.dds[usize::from(channel)].set_single_tone_profile_frequency(profile, frequency) + } - pub fn set_channel_single_tone_profile_phase(&mut self, channel: u8, profile: u8, phase: f64)-> Result<(), Error> { - if channel >= 4 || profile >= 8 || phase >= 360.0 || phase < 0.0 { - return Err(Error::ParameterError); - } - self.dds[usize::from(channel)].set_single_tone_profile_phase(profile, phase) - } + pub fn set_channel_single_tone_profile_phase(&mut self, channel: u8, profile: u8, phase: f64)-> Result<(), Error> { + if channel >= 4 || profile >= 8 || phase >= 360.0 || phase < 0.0 { + return Err(Error::ParameterError); + } + self.dds[usize::from(channel)].set_single_tone_profile_phase(profile, phase) + } - pub fn set_channel_single_tone_profile_amplitude(&mut self, channel: u8, profile: u8, amplitude: f64)-> Result<(), Error> { - if channel >= 4 || profile >= 8 || amplitude < 0.0 || amplitude > 1.0 { - return Err(Error::ParameterError); - } - self.dds[usize::from(channel)].set_single_tone_profile_amplitude(profile, amplitude) - } + pub fn set_channel_single_tone_profile_amplitude(&mut self, channel: u8, profile: u8, amplitude: f64)-> Result<(), Error> { + if channel >= 4 || profile >= 8 || amplitude < 0.0 || amplitude > 1.0 { + return Err(Error::ParameterError); + } + self.dds[usize::from(channel)].set_single_tone_profile_amplitude(profile, amplitude) + } - pub fn set_channel_sys_clk(&mut self, channel: u8, f_sys_clk: f64) -> Result<(), Error> { - self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk).map(|_| ()) - } + pub fn set_channel_sys_clk(&mut self, channel: u8, f_sys_clk: f64) -> Result<(), Error> { + self.dds[usize::from(channel)].set_sys_clk_frequency(f_sys_clk).map(|_| ()) + } - // Multi-dds channel functions - // Do not allow reading of DDS registers - // Make sure only 1 SPI transaction is compelted per function call + // Multi-dds channel functions + // Do not allow reading of DDS registers + // Make sure only 1 SPI transaction is compelted per function call - // Setup NU_MASK in configuration register - // This selects the DDS channels that will be covered by multi_channel DDS (spi3) - // Note: If a channel is masked, io_update must be completed through configuration register (IO_UPDATE bit-field) - // Implication: Deselect such channel if individual communication is needed. - pub fn set_multi_channel_coverage(&mut self, channel: u8) -> Result<(), Error> { - self.config_register.set_configurations(&mut [ - (CFGMask::MASK_NU, channel.into()) - ]).map(|_| ()) - } + // Setup NU_MASK in configuration register + // This selects the DDS channels that will be covered by multi_channel DDS (spi3) + // Note: If a channel is masked, io_update must be completed through configuration register (IO_UPDATE bit-field) + // Implication: Deselect such channel if individual communication is needed. + pub fn set_multi_channel_coverage(&mut self, channel: u8) -> Result<(), Error> { + self.config_register.set_configurations(&mut [ + (CFGMask::MASK_NU, channel.into()) + ]).map(|_| ()) + } - // Difference from individual single tone setup function: - // - Remove the need of passing channel - // All selected channels must share the same f_sys_clk - pub fn set_multi_channel_single_tone_profile(&mut self, profile: u8, frequency: f64, phase: f64, amplitude: f64) -> Result<(), Error> { - if profile >= 8 || frequency < 0.0 || phase >= 360.0 || - phase < 0.0 || amplitude < 0.0 || amplitude > 1.0 { - return Err(Error::ParameterError); - } - // Check f_sys_clk of all selected channels - let selected_channels = self.config_register.get_configuration(CFGMask::MASK_NU); - let mut found_a_selected_channel = false; - let mut reported_f_sys_clk: f64 = 0.0; - for channel_bit in 0..4 { - if (selected_channels & (1 << (channel_bit as u8))) != 0 { - if !found_a_selected_channel { - found_a_selected_channel = true; - reported_f_sys_clk = self.dds[channel_bit].get_f_sys_clk(); - } else if reported_f_sys_clk != self.dds[channel_bit].get_f_sys_clk() { - return Err(Error::DDSError); - } - } - } - self.multi_dds.set_sys_clk_frequency(reported_f_sys_clk); - self.multi_dds.set_single_tone_profile(profile, frequency, phase, amplitude)?; - self.invoke_io_update()?; - Ok(()) - } - - // Generate a pulse for io_update bit in configuration register - // This acts like io_update in CPLD struct, but for multi-dds channel - fn invoke_io_update(&mut self) -> Result<(), Error> { - self.config_register.set_configurations(&mut [ - (CFGMask::IO_UPDATE, 1) - ])?; - self.config_register.set_configurations(&mut [ - (CFGMask::IO_UPDATE, 0) - ]).map(|_| ()) - } + // Difference from individual single tone setup function: + // - Remove the need of passing channel + // All selected channels must share the same f_sys_clk + pub fn set_multi_channel_single_tone_profile(&mut self, profile: u8, frequency: f64, phase: f64, amplitude: f64) -> Result<(), Error> { + if profile >= 8 || frequency < 0.0 || phase >= 360.0 || + phase < 0.0 || amplitude < 0.0 || amplitude > 1.0 { + return Err(Error::ParameterError); + } + // Check f_sys_clk of all selected channels + let selected_channels = self.config_register.get_configuration(CFGMask::MASK_NU); + let mut found_a_selected_channel = false; + let mut reported_f_sys_clk: f64 = 0.0; + for channel_bit in 0..4 { + if (selected_channels & (1 << (channel_bit as u8))) != 0 { + if !found_a_selected_channel { + found_a_selected_channel = true; + reported_f_sys_clk = self.dds[channel_bit].get_f_sys_clk(); + } else if reported_f_sys_clk != self.dds[channel_bit].get_f_sys_clk() { + return Err(Error::DDSError); + } + } + } + self.multi_dds.set_sys_clk_frequency(reported_f_sys_clk)?; + self.multi_dds.set_single_tone_profile(profile, frequency, phase, amplitude)?; + self.invoke_io_update()?; + Ok(()) + } + // Generate a pulse for io_update bit in configuration register + // This acts like io_update in CPLD struct, but for multi-dds channel + fn invoke_io_update(&mut self) -> Result<(), Error> { + self.config_register.set_configurations(&mut [ + (CFGMask::IO_UPDATE, 1) + ])?; + self.config_register.set_configurations(&mut [ + (CFGMask::IO_UPDATE, 0) + ]).map(|_| ()) + } }