From f60ec09b2937d95c7a949a772672940795ea6559 Mon Sep 17 00:00:00 2001 From: occheung Date: Mon, 31 Aug 2020 16:48:21 +0800 Subject: [PATCH] scpi: implement rst --- examples/ethernet.rs | 4 +- src/dds.rs | 116 ++++++++++++++++++++++++++++++++++--------- src/lib.rs | 43 ++++++++++++++++ src/scpi.rs | 14 +++++- 4 files changed, 150 insertions(+), 27 deletions(-) diff --git a/examples/ethernet.rs b/examples/ethernet.rs index c592d61..1522f08 100644 --- a/examples/ethernet.rs +++ b/examples/ethernet.rs @@ -130,8 +130,8 @@ fn main() -> ! { let ccdr = rcc .sys_ck(200.mhz()) .hclk(200.mhz()) - .pll1_r_ck(100.mhz()) // for TRACECK - .pll1_q_ck(48.mhz()) + .pll1_r_ck(100.mhz()) // for TRACECK + .pll1_q_ck(48.mhz()) // for SPI .freeze(vos, &dp.SYSCFG); // Get the delay provider. diff --git a/src/dds.rs b/src/dds.rs index 7e82482..f003a3c 100644 --- a/src/dds.rs +++ b/src/dds.rs @@ -144,33 +144,36 @@ where let divider = f_sys_clk / self.f_ref_clk; // Reject extreme divider values. However, accept no frequency division if ((divider > 127 || divider < 12) && divider != 1) { - panic!("Invalid divider value for PLL!"); + // panic!("Invalid divider value for PLL!"); + return Err(Error::DDSCLKError); } - // Select a VCO - let vco = if divider == 1 { - 6 // Bypass PLL if no frequency division needed - } else if f_sys_clk > 1_150_000_000 { - panic!("Invalid divider value for PLL!") - } else if f_sys_clk > 820_000_000 { - 5 - } else if f_sys_clk > 700_000_000 { - 4 - } else if f_sys_clk > 600_000_000 { - 3 - } else if f_sys_clk > 500_000_000 { - 2 - } else if f_sys_clk > 420_000_000 { - 1 - } else if f_sys_clk > 370_000_000 { - 0 - } else { - 7 // Bypass PLL if f_sys_clk is too low - }; + // // Select a VCO + // let vco = if divider == 1 { + // 6 // Bypass PLL if no frequency division needed + // } else if f_sys_clk > 1_150_000_000 { + // panic!("Invalid divider value for PLL!") + // } else if f_sys_clk > 820_000_000 { + // 5 + // } else if f_sys_clk > 700_000_000 { + // 4 + // } else if f_sys_clk > 600_000_000 { + // 3 + // } else if f_sys_clk > 500_000_000 { + // 2 + // } else if f_sys_clk > 420_000_000 { + // 1 + // } else if f_sys_clk > 370_000_000 { + // 0 + // } else { + // 7 // Bypass PLL if f_sys_clk is too low + // }; + 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), + (DDSCFRMask::VCO_SEL, vco.into()), // Reset PLL lock before re-enabling it (DDSCFRMask::PFD_RESET, 1), ])?; @@ -182,9 +185,76 @@ where } // Change external clock source (ref_clk) - pub fn set_ref_clk_frequency(&mut self, f_ref_clk: u64) { + pub fn set_ref_clk_frequency(&mut self, f_ref_clk: u64) -> Result<(), Error> { self.f_ref_clk = f_ref_clk; // TODO: Examine clock tree and update f_sys_clk + 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 :u64 = 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; + Ok(()) + } + else { + self.f_sys_clk = self.f_ref_clk; + Ok(()) + } + } + + fn get_VCO_no(&mut self, f_sys_clk: u64, 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 { + Err(Error::DDSCLKError) + } else if f_sys_clk > 820_000_000 { + Ok(5) + } else if f_sys_clk > 700_000_000 { + Ok(4) + } else if f_sys_clk > 600_000_000 { + Ok(3) + } else if f_sys_clk > 500_000_000 { + Ok(2) + } else if f_sys_clk > 420_000_000 { + Ok(1) + } else if f_sys_clk > 370_000_000 { + Ok(0) + } else { + Ok(7) // Bypass PLL if f_sys_clk is too low + } } /* diff --git a/src/lib.rs b/src/lib.rs index 53642f2..a6a4c69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,7 @@ use crate::cpld::DoOnGetRefMutData; pub mod config_register; use crate::config_register::ConfigRegister; +use crate::config_register::CFGMask; pub mod attenuator; use crate::attenuator::Attenuator; @@ -46,6 +47,8 @@ pub enum Error { AttenuatorError, IOUpdateError, DDSError, + ConfigRegisterError, + DDSCLKError, } /* @@ -78,6 +81,46 @@ where ], } } + + /* + * 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. CPLD divides clock frequency by 4 by default. + for chip_no in 0..4 { + self.dds[chip_no].set_ref_clk_frequency(25_000_000); + } + Ok(()) + } } // /* diff --git a/src/scpi.rs b/src/scpi.rs index d97eb30..aed4305 100644 --- a/src/scpi.rs +++ b/src/scpi.rs @@ -28,6 +28,8 @@ use scpi::{ scpi_tree, }; +use embedded_hal::blocking::spi::Transfer; + use crate::Urukul; // pub struct MyDevice; @@ -50,13 +52,21 @@ impl Command for HelloWorldCommand { * Implement "Device" trait from SCPI * TODO: Implement mandatory commands */ - impl Device for Urukul { +impl Device for Urukul +where + SPI: Transfer +{ fn cls(&mut self) -> Result<()> { Ok(()) } fn rst(&mut self) -> Result<()> { - Ok(()) + match self.reset() { + Ok(_) => Ok(()), + Err(_) => Err(Error::new( + ErrorCode::HardwareError + )) + } } }