420: Miscellaneous r=jordens a=jordens

* dds: name consistently
* dds: inline
* refactor the broker ip parsing to be panicing
* speed up qspi to 100 MHz (tested)
* tweak pounder qspi xfer assembly padding for speed
* fix some clippy lints
* try 32 byte max qspi xfer size instead of 16 but revert that until the memclr that's being pulled is can be avoided.

Co-authored-by: Robert Jördens <rj@quartiq.de>
master
bors[bot] 2021-08-04 10:52:37 +00:00 committed by GitHub
commit d5a7796876
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 70 deletions

View File

@ -34,6 +34,7 @@ pub trait Interface {
/// Indicates various communication modes of the DDS. The value of this enumeration is equivalent to
/// the configuration bits of the DDS CSR register.
#[derive(Copy, Clone, PartialEq)]
#[repr(u8)]
pub enum Mode {
SingleBitTwoWire = 0b00,
SingleBitThreeWire = 0b01,
@ -320,7 +321,7 @@ impl<I: Interface> Ad9959<I> {
.write(Register::CSR as u8, &[csr])
.map_err(|_| Error::Interface)?;
self.write(register, &data)?;
self.write(register, data)?;
Ok(())
}
@ -528,7 +529,7 @@ pub struct DdsConfig {
impl DdsConfig {
/// Create a serializer that can be used for generating a serialized DDS profile for writing to
/// a QSPI stream.
pub fn builder(&self) -> ProfileSerializer {
pub fn serializer(&self) -> ProfileSerializer {
ProfileSerializer::new(self.mode)
}
}
@ -562,6 +563,7 @@ impl ProfileSerializer {
/// * `acr` - If provided, indicates the amplitude control register for the channels. The ACR
/// should be stored in the 3 LSB of the word. Note that if amplitude scaling is to be used,
/// the "Amplitude multiplier enable" bit must be set.
#[inline]
pub fn update_channels(
&mut self,
channels: &[Channel],
@ -585,7 +587,7 @@ impl ProfileSerializer {
}
if let Some(acr) = acr {
self.add_write(Register::ACR, &acr.to_be_bytes()[1..=3]);
self.add_write(Register::ACR, &acr.to_be_bytes()[1..]);
}
}
@ -597,20 +599,20 @@ impl ProfileSerializer {
self.index += value.len() + 1;
}
#[inline]
fn pad(&mut self) {
// Pad the buffer to 32-bit (4 byte) alignment by adding dummy writes to CSR and LSRR.
match self.index & 3 {
3 => {
// For a level of 3, we have to pad with 5 bytes to align things.
self.add_write(Register::CSR, &[(self.mode as u8) << 1]);
self.add_write(Register::LSRR, &[0, 0]);
}
2 => self.add_write(Register::CSR, &[(self.mode as u8) << 1]),
1 => self.add_write(Register::LSRR, &[0, 0]),
0 => {}
_ => unreachable!(),
// In the case of 1 byte padding, this instead pads with 5 bytes as there is no
// valid single-byte write that could be used.
if self.index & 1 != 0 {
// Pad with 3 bytes
self.add_write(Register::LSRR, &[0, 0]);
}
if self.index & 2 != 0 {
// Pad with 2 bytes
self.add_write(Register::CSR, &[(self.mode as u8) << 1]);
}
debug_assert_eq!(self.index & 3, 0);
}
/// Get the serialized profile as a slice of 32-bit words.
@ -621,6 +623,7 @@ impl ProfileSerializer {
///
/// # Returns
/// A slice of `u32` words representing the serialized profile.
#[inline]
pub fn finalize<'a>(&'a mut self) -> &'a [u32] {
self.pad();
unsafe {

View File

@ -47,7 +47,6 @@ use stabilizer::{
DigitalInput0, DigitalInput1, AFE0, AFE1,
},
net::{
self,
data_stream::{FrameGenerator, StreamFormat, StreamTarget},
miniconf::Miniconf,
serde::Deserialize,
@ -200,7 +199,10 @@ const APP: () = {
stabilizer.cycle_counter,
env!("CARGO_BIN_NAME"),
stabilizer.net.mac_address,
net::parse_or_default_broker(option_env!("BROKER")),
option_env!("BROKER")
.unwrap_or("10.34.16.10")
.parse()
.unwrap(),
);
let generator = network

View File

@ -50,7 +50,6 @@ use stabilizer::{
DigitalInput0, DigitalInput1, AFE0, AFE1,
},
net::{
self,
data_stream::{FrameGenerator, StreamFormat, StreamTarget},
miniconf::Miniconf,
serde::Deserialize,
@ -241,7 +240,10 @@ const APP: () = {
stabilizer.cycle_counter,
env!("CARGO_BIN_NAME"),
stabilizer.net.mac_address,
net::parse_or_default_broker(option_env!("BROKER")),
option_env!("BROKER")
.unwrap_or("10.34.16.10")
.parse()
.unwrap(),
);
let generator = network.configure_streaming(

View File

@ -11,13 +11,13 @@ pub const ADC_DAC_SCK_MAX: MegaHertz = MegaHertz(50);
pub const TIMER_FREQUENCY: MegaHertz = MegaHertz(100);
/// The QSPI frequency for communicating with the pounder DDS.
pub const POUNDER_QSPI_FREQUENCY: MegaHertz = MegaHertz(40);
pub const POUNDER_QSPI_FREQUENCY: MegaHertz = MegaHertz(100);
/// The delay after initiating a QSPI transfer before asserting the IO_Update for the pounder DDS.
// Pounder Profile writes are always 16 bytes, with 2 cycles required per byte, coming out to a
// total of 32 QSPI clock cycles. The QSPI is configured for 40MHz, so this comes out to an offset
// of 800nS. We use 900ns to be safe.
pub const POUNDER_IO_UPDATE_DELAY: f32 = 900e-9;
// Pounder Profile writes are up to 16 bytes, with 2 cycles required per byte, coming out to a
// total of 32 QSPI clock cycles. The QSPI is configured for 100MHz, so this comes out to an offset
// of 320 ns. We use 400 ns to be safe.
pub const POUNDER_IO_UPDATE_DELAY: f32 = 400e-9;
/// The duration to assert IO_Update for the pounder DDS.
// IO_Update should be latched for 4 SYNC_CLK cycles after the QSPI profile write. With pounder

View File

@ -76,15 +76,15 @@ impl DdsOutput {
/// # Args
/// * `qspi` - The QSPI interface to the run the stream on.
/// * `io_update_trigger` - The HighResTimerE used to generate IO_Update pulses.
/// * `dds_config` - The frozen DDS configuration.
/// * `config` - The frozen DDS configuration.
pub fn new(
mut qspi: QspiInterface,
io_update_trigger: HighResTimerE,
dds_config: DdsConfig,
config: DdsConfig,
) -> Self {
qspi.start_stream().unwrap();
Self {
config: dds_config,
config,
_qspi: qspi,
io_update_trigger,
}
@ -93,10 +93,10 @@ impl DdsOutput {
/// Get a builder for serializing a Pounder DDS profile.
#[allow(dead_code)]
pub fn builder(&mut self) -> ProfileBuilder {
let builder = self.config.builder();
let serializer = self.config.serializer();
ProfileBuilder {
dds_stream: self,
serializer: builder,
dds_output: self,
serializer,
}
}
@ -135,7 +135,7 @@ impl DdsOutput {
/// A temporary builder for serializing and writing profiles.
pub struct ProfileBuilder<'a> {
dds_stream: &'a mut DdsOutput,
dds_output: &'a mut DdsOutput,
serializer: ProfileSerializer,
}
@ -162,8 +162,9 @@ impl<'a> ProfileBuilder<'a> {
/// Write the profile to the DDS asynchronously.
#[allow(dead_code)]
#[inline]
pub fn write_profile(mut self) {
let profile = self.serializer.finalize();
self.dds_stream.write_profile(profile);
self.dds_output.write_profile(profile);
}
}

View File

@ -233,7 +233,7 @@ impl ad9959::Interface for QspiInterface {
};
self.qspi
.write(encoded_address.into(), &encoded_payload)
.write(encoded_address.into(), encoded_payload)
.map_err(|_| Error::Qspi)
}
ad9959::Mode::FourBitSerial => {

View File

@ -207,6 +207,8 @@ pub fn setup(
.d2ccip1r
.modify(|_, w| w.spi123sel().pll2_p().spi45sel().pll2_q());
device.RCC.d1ccipr.modify(|_, w| w.qspisel().rcc_hclk3());
let rcc = device.RCC.constrain();
let ccdr = rcc
.use_hse(8.mhz())

View File

@ -297,21 +297,23 @@ macro_rules! timer_channels {
// Only atomic operations on completed on the timer registers.
let regs = unsafe { &*<$TY>::ptr() };
let result = if regs.sr.read().[< cc $index if >]().bit_is_set() {
if regs.sr.read().[< cc $index if >]().bit_is_set() {
// Read the capture value. Reading the captured value clears the flag in the
// status register automatically.
Some(regs.[< ccr $index >].read().ccr().bits())
} else {
None
};
let result = regs.[< ccr $index >].read().ccr().bits();
// Read SR again to check for a potential over-capture. If there is an
// overcapture, return an error.
if regs.sr.read().[< cc $index of >]().bit_is_set() {
regs.sr.modify(|_, w| w.[< cc $index of >]().clear_bit());
Err(result)
// Read SR again to check for a potential over-capture. Return an error in
// that case.
let sr = regs.sr.read();
if sr.[< cc $index of >]().bit_is_set() {
// NOTE(unsafe) write-back is safe
regs.sr.write(|w| unsafe { w.bits(sr.bits()) }.[< cc $index of >]().clear_bit());
Err(Some(result))
} else {
Ok(Some(result))
}
} else {
Ok(result)
Ok(None)
}
}

View File

@ -208,30 +208,3 @@ pub fn get_device_prefix(
prefix
}
/// Determine the broker IP address
///
/// # Note
/// If the broker IP is unspecified or unparseable, the default IP is returned.
///
/// # Args
/// * `input` - The optionally-specified command-line broker IP address as a string.
///
/// # Returns
/// The broker IP address.
pub fn parse_or_default_broker(input: Option<&str>) -> IpAddr {
input.and_then(|data| {
data.parse::<minimq::embedded_nal::IpAddr>().map_or_else(
|err| {
log::error!(
"{:?}: Failed to parse broker IP ({:?}) - Falling back to default",
err,
data
);
None
},
Some,
)
})
.unwrap_or_else(|| DEFAULT_MQTT_BROKER.into())
}