forked from M-Labs/humpback-dds
scpi: implement rst
This commit is contained in:
parent
d78f85721f
commit
f60ec09b29
@ -130,8 +130,8 @@ fn main() -> ! {
|
|||||||
let ccdr = rcc
|
let ccdr = rcc
|
||||||
.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.
|
||||||
|
116
src/dds.rs
116
src/dds.rs
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
43
src/lib.rs
43
src/lib.rs
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /*
|
// /*
|
||||||
|
14
src/scpi.rs
14
src/scpi.rs
@ -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
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user