scpi: implement rst

This commit is contained in:
occheung 2020-08-31 16:48:21 +08:00
parent d78f85721f
commit f60ec09b29
4 changed files with 150 additions and 27 deletions

View File

@ -131,7 +131,7 @@ fn main() -> ! {
.sys_ck(200.mhz()) .sys_ck(200.mhz())
.hclk(200.mhz()) .hclk(200.mhz())
.pll1_r_ck(100.mhz()) // for TRACECK .pll1_r_ck(100.mhz()) // for TRACECK
.pll1_q_ck(48.mhz()) .pll1_q_ck(48.mhz()) // for SPI
.freeze(vos, &dp.SYSCFG); .freeze(vos, &dp.SYSCFG);
// Get the delay provider. // Get the delay provider.

View File

@ -144,33 +144,36 @@ where
let divider = f_sys_clk / self.f_ref_clk; let divider = f_sys_clk / self.f_ref_clk;
// Reject extreme divider values. However, accept no frequency division // Reject extreme divider values. However, accept no frequency division
if ((divider > 127 || divider < 12) && divider != 1) { 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 // // Select a VCO
let vco = if divider == 1 { // let vco = if divider == 1 {
6 // Bypass PLL if no frequency division needed // 6 // Bypass PLL if no frequency division needed
} else if f_sys_clk > 1_150_000_000 { // } else if f_sys_clk > 1_150_000_000 {
panic!("Invalid divider value for PLL!") // panic!("Invalid divider value for PLL!")
} else if f_sys_clk > 820_000_000 { // } else if f_sys_clk > 820_000_000 {
5 // 5
} else if f_sys_clk > 700_000_000 { // } else if f_sys_clk > 700_000_000 {
4 // 4
} else if f_sys_clk > 600_000_000 { // } else if f_sys_clk > 600_000_000 {
3 // 3
} else if f_sys_clk > 500_000_000 { // } else if f_sys_clk > 500_000_000 {
2 // 2
} else if f_sys_clk > 420_000_000 { // } else if f_sys_clk > 420_000_000 {
1 // 1
} else if f_sys_clk > 370_000_000 { // } else if f_sys_clk > 370_000_000 {
0 // 0
} else { // } else {
7 // Bypass PLL if f_sys_clk is too low // 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 [ self.set_configurations(&mut [
// Enable PLL, set divider (valid or not) and VCO // Enable PLL, set divider (valid or not) and VCO
(DDSCFRMask::PLL_ENABLE, 1), (DDSCFRMask::PLL_ENABLE, 1),
(DDSCFRMask::N, divider as u32), (DDSCFRMask::N, divider as u32),
(DDSCFRMask::VCO_SEL, vco), (DDSCFRMask::VCO_SEL, vco.into()),
// Reset PLL lock before re-enabling it // Reset PLL lock before re-enabling it
(DDSCFRMask::PFD_RESET, 1), (DDSCFRMask::PFD_RESET, 1),
])?; ])?;
@ -182,9 +185,76 @@ where
} }
// Change external clock source (ref_clk) // 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<E>> {
self.f_ref_clk = f_ref_clk; self.f_ref_clk = f_ref_clk;
// TODO: Examine clock tree and update f_sys_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<u8, Error<E>> {
// 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
}
} }
/* /*

View File

@ -26,6 +26,7 @@ use crate::cpld::DoOnGetRefMutData;
pub mod config_register; pub mod config_register;
use crate::config_register::ConfigRegister; use crate::config_register::ConfigRegister;
use crate::config_register::CFGMask;
pub mod attenuator; pub mod attenuator;
use crate::attenuator::Attenuator; use crate::attenuator::Attenuator;
@ -46,6 +47,8 @@ pub enum Error<E> {
AttenuatorError, AttenuatorError,
IOUpdateError, IOUpdateError,
DDSError, 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<E>> {
// 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(())
}
} }
// /* // /*

View File

@ -28,6 +28,8 @@ use scpi::{
scpi_tree, scpi_tree,
}; };
use embedded_hal::blocking::spi::Transfer;
use crate::Urukul; use crate::Urukul;
// pub struct MyDevice; // pub struct MyDevice;
@ -50,13 +52,21 @@ impl Command for HelloWorldCommand {
* Implement "Device" trait from SCPI * Implement "Device" trait from SCPI
* TODO: Implement mandatory commands * TODO: Implement mandatory commands
*/ */
impl<SPI> Device for Urukul<SPI> { impl<SPI, E> Device for Urukul<SPI>
where
SPI: Transfer<u8, Error = E>
{
fn cls(&mut self) -> Result<()> { fn cls(&mut self) -> Result<()> {
Ok(()) Ok(())
} }
fn rst(&mut self) -> Result<()> { fn rst(&mut self) -> Result<()> {
Ok(()) match self.reset() {
Ok(_) => Ok(()),
Err(_) => Err(Error::new(
ErrorCode::HardwareError
))
}
} }
} }