From 261ed127983fb2a280c602b8b473409612e35da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 17 May 2021 12:32:20 +0200 Subject: [PATCH 01/40] dsp: reduce num dependency --- dsp/Cargo.toml | 2 +- dsp/src/complex.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsp/Cargo.toml b/dsp/Cargo.toml index 0f82bd2..e3be45b 100644 --- a/dsp/Cargo.toml +++ b/dsp/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] serde = { version = "1.0", features = ["derive"], default-features = false } -num = { version = "0.4.0", default-features = false } +num-complex = { version = "0.4.0", features = ["serde"], default-features = false } miniconf = "0.1" [dev-dependencies] diff --git a/dsp/src/complex.rs b/dsp/src/complex.rs index 1eb003f..bf3e76d 100644 --- a/dsp/src/complex.rs +++ b/dsp/src/complex.rs @@ -1,4 +1,4 @@ -pub use num::Complex; +pub use num_complex::Complex; use super::{atan2, cossin}; From e58e7f179e728e8e77f47890c58e1c36d60b21c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 17 May 2021 13:01:45 +0200 Subject: [PATCH 02/40] clean up item visibility * There isn't much API that would only be used accross modules within stabilizer/dsp but should not be pub beyond stabilizer/dsp. * Therefore it's easier to let the definition determine visibility and the mod.rs/lib.rs determine location in the namesapce. * Blanket use pub items in mod and lib. --- Cargo.lock | 38 ++--------------------------------- src/bin/dual-iir.rs | 23 ++++++++++----------- src/bin/lockin.rs | 25 +++++++++-------------- src/hardware/configuration.rs | 4 ++-- src/hardware/mod.rs | 16 +++++++-------- src/hardware/pounder/mod.rs | 18 ++++++++--------- src/net/mod.rs | 15 +++++++------- 7 files changed, 48 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 274b2bb..388ce16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,7 +208,7 @@ dependencies = [ "easybench", "miniconf", "ndarray", - "num", + "num-complex", "rand", "serde", ] @@ -458,19 +458,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2178127478ae4ee9be7180bc9c3bffb6354dd7238400db567102f98c413a9f35" -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - [[package]] name = "num-complex" version = "0.4.0" @@ -478,6 +465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" dependencies = [ "num-traits", + "serde", ] [[package]] @@ -490,28 +478,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.14" diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 29eb474..9b1511e 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -2,19 +2,18 @@ #![no_std] #![no_main] -use stabilizer::{hardware, net}; - -use miniconf::Miniconf; use serde::Deserialize; use dsp::iir; -use hardware::{ - Adc0Input, Adc1Input, AdcCode, AfeGain, Dac0Output, Dac1Output, DacCode, - DigitalInput0, DigitalInput1, InputPin, SystemTimer, AFE0, AFE1, +use stabilizer::{ + hardware::{ + hal, setup, Adc0Input, Adc1Input, AdcCode, AfeGain, Dac0Output, + Dac1Output, DacCode, DigitalInput0, DigitalInput1, InputPin, + SystemTimer, AFE0, AFE1, + }, + net::{Miniconf, NetworkUsers, Telemetry, TelemetryBuffer, UpdateState}, }; -use net::{NetworkUsers, Telemetry, TelemetryBuffer, UpdateState}; - const SCALE: f32 = i16::MAX as _; // The number of cascaded IIR biquads per channel. Select 1 or 2! @@ -50,7 +49,7 @@ impl Default for Settings { } } -#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] +#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] const APP: () = { struct Resources { afes: (AFE0, AFE1), @@ -69,7 +68,7 @@ const APP: () = { #[init(spawn=[telemetry, settings_update])] fn init(c: init::Context) -> init::LateResources { // Configure the microcontroller - let (mut stabilizer, _pounder) = hardware::setup(c.core, c.device); + let (mut stabilizer, _pounder) = setup(c.core, c.device); let network = NetworkUsers::new( stabilizer.net.stack, @@ -98,7 +97,7 @@ const APP: () = { dacs: stabilizer.dacs, network, digital_inputs: stabilizer.digital_inputs, - telemetry: net::TelemetryBuffer::default(), + telemetry: TelemetryBuffer::default(), settings: Settings::default(), } } @@ -214,7 +213,7 @@ const APP: () = { #[task(binds = ETH, priority = 1)] fn eth(_: eth::Context) { - unsafe { stm32h7xx_hal::ethernet::interrupt_handler() } + unsafe { hal::ethernet::interrupt_handler() } } #[task(binds = SPI2, priority = 3)] diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index a187767..d8a4a74 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -2,23 +2,18 @@ #![no_std] #![no_main] -use embedded_hal::digital::v2::InputPin; - use serde::Deserialize; use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL}; - -use stabilizer::net; - -use stabilizer::hardware::{ - design_parameters, setup, Adc0Input, Adc1Input, AdcCode, AfeGain, - Dac0Output, Dac1Output, DacCode, DigitalInput0, DigitalInput1, - InputStamper, SystemTimer, AFE0, AFE1, +use stabilizer::{ + hardware::{ + design_parameters, hal, setup, Adc0Input, Adc1Input, AdcCode, AfeGain, + Dac0Output, Dac1Output, DacCode, DigitalInput0, DigitalInput1, + InputPin, InputStamper, SystemTimer, AFE0, AFE1, + }, + net::{Miniconf, NetworkUsers, Telemetry, TelemetryBuffer, UpdateState}, }; -use miniconf::Miniconf; -use net::{NetworkUsers, Telemetry, TelemetryBuffer, UpdateState}; - // A constant sinusoid to send on the DAC output. // Full-scale gives a +/- 10.24V amplitude waveform. Scale it down to give +/- 1V. const ONE: i16 = ((1.0 / 10.24) * i16::MAX as f32) as _; @@ -78,7 +73,7 @@ impl Default for Settings { } } -#[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] +#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] const APP: () = { struct Resources { afes: (AFE0, AFE1), @@ -140,7 +135,7 @@ const APP: () = { network, digital_inputs: stabilizer.digital_inputs, timestamper: stabilizer.timestamper, - telemetry: net::TelemetryBuffer::default(), + telemetry: TelemetryBuffer::default(), settings, @@ -295,7 +290,7 @@ const APP: () = { #[task(binds = ETH, priority = 1)] fn eth(_: eth::Context) { - unsafe { stm32h7xx_hal::ethernet::interrupt_handler() } + unsafe { hal::ethernet::interrupt_handler() } } #[task(binds = SPI2, priority = 3)] diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index f48b3d2..da0503e 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -833,7 +833,7 @@ pub fn setup( .set_speed(hal::gpio::Speed::VeryHigh); // Configure the IO_Update signal for the DDS. - let mut hrtimer = pounder::hrtimer::HighResTimerE::new( + let mut hrtimer = pounder::HighResTimerE::new( device.HRTIM_TIME, device.HRTIM_MASTER, device.HRTIM_COMMON, @@ -845,7 +845,7 @@ pub fn setup( // is triggered after the QSPI write, which can take approximately 120nS, so // there is additional margin. hrtimer.configure_single_shot( - pounder::hrtimer::Channel::Two, + pounder::HRTimerChannel::Two, design_parameters::POUNDER_IO_UPDATE_DURATION, design_parameters::POUNDER_IO_UPDATE_DELAY, ); diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 66152e0..4f65f94 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -1,5 +1,5 @@ ///! Module for all hardware-specific setup of Stabilizer -use stm32h7xx_hal as hal; +pub use stm32h7xx_hal as hal; // Re-export for the DigitalInputs below: pub use embedded_hal::digital::v2::InputPin; @@ -16,13 +16,13 @@ pub mod pounder; mod system_timer; mod timers; -pub use adc::{Adc0Input, Adc1Input, AdcCode}; -pub use afe::Gain as AfeGain; -pub use cycle_counter::CycleCounter; -pub use dac::{Dac0Output, Dac1Output, DacCode}; -pub use digital_input_stamper::InputStamper; -pub use pounder::DdsOutput; -pub use system_timer::SystemTimer; +pub use adc::*; +pub use afe::{Gain as AfeGain, *}; +pub use cycle_counter::*; +pub use dac::*; +pub use digital_input_stamper::*; +pub use pounder::*; +pub use system_timer::*; // Type alias for the analog front-end (AFE) for ADC0. pub type AFE0 = afe::ProgrammableGainAmplifier< diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 32a8e80..1eb60c0 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -1,21 +1,19 @@ +use super::hal; +use embedded_hal::{adc::OneShot, blocking::spi::Transfer}; use serde::{Deserialize, Serialize}; -pub mod attenuators; +mod attenuators; mod dds_output; -pub mod hrtimer; +mod hrtimer; mod rf_power; #[cfg(feature = "pounder_v1_1")] pub mod timestamp; -pub use dds_output::DdsOutput; - -use super::hal; - -use attenuators::AttenuatorInterface; -use rf_power::PowerMeasurementInterface; - -use embedded_hal::{adc::OneShot, blocking::spi::Transfer}; +pub use attenuators::*; +pub use dds_output::*; +pub use hrtimer::{Channel as HRTimerChannel, *}; +pub use rf_power::*; const EXT_CLK_SEL_PIN: u8 = 8 + 7; #[allow(dead_code)] diff --git a/src/net/mod.rs b/src/net/mod.rs index 38499ca..9843e46 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -5,11 +5,10 @@ ///! telemetry (via MQTT), configuration of run-time settings (via MQTT + Miniconf), and live data ///! streaming over raw UDP/TCP sockets. This module encompasses the main processing routines ///! related to Stabilizer networking operations. -use heapless::{consts, String}; -use miniconf::Miniconf; -use serde::Serialize; - use core::fmt::Write; +use heapless::{consts, String}; +pub use miniconf::Miniconf; +use serde::Serialize; mod messages; mod miniconf_client; @@ -20,10 +19,10 @@ mod telemetry; use crate::hardware::{CycleCounter, EthernetPhy, NetworkStack}; use messages::{MqttMessage, SettingsResponse}; -pub use miniconf_client::MiniconfClient; -pub use network_processor::NetworkProcessor; -pub use shared::NetworkManager; -pub use telemetry::{Telemetry, TelemetryBuffer, TelemetryClient}; +pub use miniconf_client::*; +pub use network_processor::*; +pub use shared::*; +pub use telemetry::*; pub type NetworkReference = shared::NetworkStackProxy<'static, NetworkStack>; From 3165c680d63c1f2307964b3dad1e17e4261a49ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 24 May 2021 20:06:31 +0200 Subject: [PATCH 03/40] dma: don't swap buffers * This uses a new closure-based method to the DMA HAL implementation which gives access to the inactive buffer directly. * It removes changing addresses, the third buffer for DBM, the inactive address poisoning, and allows the cancellation of the redundant repeat memory barriers and compiler fences. * This is now around 20 instructions per buffer down from about 100 cycles before. * Also introduces a new `SampleBuffer` type alias. * The required unpacking of the resources structure is a bit annoying but could probably abstraced away. TODO: * Test * Adapt `lockin` --- Cargo.lock | 3 +- Cargo.toml | 4 +- src/bin/dual-iir.rs | 100 ++++++++++++++++++------------ src/hardware/adc.rs | 65 +++++++------------ src/hardware/dac.rs | 43 +++++-------- src/hardware/design_parameters.rs | 2 + 6 files changed, 107 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 274b2bb..49ac680 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -802,8 +802,7 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67034b80041bc33a48df1c1c435b6ae3bb18c35e42aa7e702ce8363b96793398" +source = "git+https://github.com/quartiq/stm32h7xx-hal.git?rev=cca4ecc#cca4ecc3e0cc8cb2f7a9652c4099d50b44977493" dependencies = [ "bare-metal 1.0.0", "cast", diff --git a/Cargo.toml b/Cargo.toml index 790363a..0d84c27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,9 @@ rev = "523d71d" [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] -version = "0.9.0" +# version = "0.9.0" +git = "https://github.com/quartiq/stm32h7xx-hal.git" +rev = "cca4ecc" [patch.crates-io.miniconf] git = "https://github.com/quartiq/miniconf.git" diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 29eb474..dec0697 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -2,6 +2,8 @@ #![no_std] #![no_main] +use core::sync::atomic::{fence, Ordering}; + use stabilizer::{hardware, net}; use miniconf::Miniconf; @@ -120,51 +122,73 @@ const APP: () = { /// Because the ADC and DAC operate at the same rate, these two constraints actually implement /// the same time bounds, meeting one also means the other is also met. #[task(binds=DMA1_STR4, resources=[adcs, digital_inputs, dacs, iir_state, settings, telemetry], priority=2)] - fn process(c: process::Context) { - let adc_samples = [ - c.resources.adcs.0.acquire_buffer(), - c.resources.adcs.1.acquire_buffer(), - ]; + fn process(mut c: process::Context) { + let adc0 = &mut c.resources.adcs.0; + let adc1 = &mut c.resources.adcs.1; + let dac0 = &mut c.resources.dacs.0; + let dac1 = &mut c.resources.dacs.1; + let di = &c.resources.digital_inputs; + let settings = &c.resources.settings; + let iir_state = &mut c.resources.iir_state; + let telemetry = &mut c.resources.telemetry; + adc0.with_buffer(|a0| { + adc1.with_buffer(|a1| { + dac0.with_buffer(|d0| { + dac1.with_buffer(|d1| { + let adc_samples = [a0, a1]; + let dac_samples = [d0, d1]; - let dac_samples = [ - c.resources.dacs.0.acquire_buffer(), - c.resources.dacs.1.acquire_buffer(), - ]; + let digital_inputs = + [di.0.is_high().unwrap(), di.1.is_high().unwrap()]; - let digital_inputs = [ - c.resources.digital_inputs.0.is_high().unwrap(), - c.resources.digital_inputs.1.is_high().unwrap(), - ]; + let hold = settings.force_hold + || (digital_inputs[1] && settings.allow_hold); - let hold = c.resources.settings.force_hold - || (digital_inputs[1] && c.resources.settings.allow_hold); + // Preserve instruction and data ordering w.r.t. DMA flag access. + fence(Ordering::SeqCst); - for channel in 0..adc_samples.len() { - for sample in 0..adc_samples[0].len() { - let mut y = f32::from(adc_samples[channel][sample] as i16); - for i in 0..c.resources.iir_state[channel].len() { - y = c.resources.settings.iir_ch[channel][i].update( - &mut c.resources.iir_state[channel][i], - y, - hold, - ); - } - // Note(unsafe): The filter limits ensure that the value is in range. - // The truncation introduces 1/2 LSB distortion. - let y = unsafe { y.to_int_unchecked::() }; - // Convert to DAC code - dac_samples[channel][sample] = DacCode::from(y).0; - } - } + for channel in 0..adc_samples.len() { + adc_samples[channel] + .iter() + .zip(dac_samples[channel].iter_mut()) + .map(|(ai, di)| { + let x = f32::from(*ai as i16); + let y = settings.iir_ch[channel] + .iter() + .zip(iir_state[channel].iter_mut()) + .fold(x, |yi, (ch, state)| { + ch.update(state, yi, hold) + }); + // Note(unsafe): The filter limits must ensure that + // the value is in range. + // The truncation introduces 1/2 LSB distortion. + let y: i16 = + unsafe { y.to_int_unchecked() }; + // Convert to DAC code + *di = DacCode::from(y).0; + }) + .last(); + } - // Update telemetry measurements. - c.resources.telemetry.adcs = - [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])]; + // Update telemetry measurements. + telemetry.adcs = [ + AdcCode(adc_samples[0][0]), + AdcCode(adc_samples[1][0]), + ]; - c.resources.telemetry.dacs = - [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])]; + telemetry.dacs = [ + DacCode(dac_samples[0][0]), + DacCode(dac_samples[1][0]), + ]; - c.resources.telemetry.digital_inputs = digital_inputs; + telemetry.digital_inputs = digital_inputs; + + // Preserve instruction and data ordering w.r.t. DMA flag access. + fence(Ordering::SeqCst); + }) + }) + }) + }); } #[idle(resources=[network], spawn=[settings_update])] diff --git a/src/hardware/adc.rs b/src/hardware/adc.rs index 9289097..101d762 100644 --- a/src/hardware/adc.rs +++ b/src/hardware/adc.rs @@ -29,15 +29,9 @@ ///! available. When enough samples have been collected, a transfer-complete interrupt is generated ///! and the ADC samples are available for processing. ///! -///! The SPI peripheral internally has an 8- or 16-byte TX and RX FIFO, which corresponds to a 4- or -///! 8-sample buffer for incoming ADC samples. During the handling of the DMA transfer completion, -///! there is a small window where buffers are swapped over where it's possible that a sample could -///! be lost. In order to avoid this, the SPI RX FIFO is effectively used as a "sample overflow" -///! region and can buffer a number of samples until the next DMA transfer is configured. If a DMA -///! transfer is still not set in time, the SPI peripheral will generate an input-overrun interrupt. -///! This interrupt then serves as a means of detecting if samples have been lost, which will occur -///! whenever data processing takes longer than the collection period. -///! +///! After a complete transfer of a batch of samples, the inactive buffer is available to the +///! user for processing. The processing must complete before the DMA transfer of the next batch +///! completes. ///! ///! ## Starting Data Collection ///! @@ -68,13 +62,12 @@ ///! sample DMA requests, which can be completed by setting e.g. ADC0's comparison to a counter ///! value of 0 and ADC1's comparison to a counter value of 1. ///! -///! In this implementation, single buffer mode DMA transfers are used because the SPI RX FIFO can -///! be used as a means to both detect and buffer ADC samples during the buffer swap-over. Because -///! of this, double-buffered mode does not offer any advantages over single-buffered mode (unless -///! double-buffered mode offers less overhead due to the DMA disable/enable procedure). +///! In this implementation, double buffer mode DMA transfers are used because the SPI RX FIFOs +///! have finite depth, FIFO access is slower than AXISRAM access, and because the single +///! buffer mode DMA disable/enable and buffer update sequence is slow. use stm32h7xx_hal as hal; -use super::design_parameters::SAMPLE_BUFFER_SIZE; +use super::design_parameters::{SampleBuffer, SAMPLE_BUFFER_SIZE}; use super::timers; use hal::dma::{ @@ -119,8 +112,7 @@ static mut SPI_EOT_CLEAR: [u32; 1] = [0x00]; // processed). Note that the contents of AXI SRAM is uninitialized, so the buffer contents on // startup are undefined. The dimensions are `ADC_BUF[adc_index][ping_pong_index][sample_index]`. #[link_section = ".axisram.buffers"] -static mut ADC_BUF: [[[u16; SAMPLE_BUFFER_SIZE]; 2]; 2] = - [[[0; SAMPLE_BUFFER_SIZE]; 2]; 2]; +static mut ADC_BUF: [[SampleBuffer; 2]; 2] = [[[0; SAMPLE_BUFFER_SIZE]; 2]; 2]; macro_rules! adc_input { ($name:ident, $index:literal, $trigger_stream:ident, $data_stream:ident, $clear_stream:ident, @@ -192,12 +184,11 @@ macro_rules! adc_input { /// Represents data associated with ADC. pub struct $name { - next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>, transfer: Transfer< hal::dma::dma::$data_stream, hal::spi::Spi, PeripheralToMemory, - &'static mut [u16; SAMPLE_BUFFER_SIZE], + &'static mut SampleBuffer, hal::dma::DBTransfer, >, trigger_transfer: Transfer< @@ -316,6 +307,7 @@ macro_rules! adc_input { // data stream is used to trigger a transfer completion interrupt. let data_config = DmaConfig::default() .memory_increment(true) + .double_buffer(true) .transfer_complete_interrupt($index == 1) .priority(Priority::VeryHigh); @@ -333,17 +325,14 @@ macro_rules! adc_input { Transfer::init( data_stream, spi, - // Note(unsafe): The ADC_BUF[$index][0] is "owned" by this peripheral. + // Note(unsafe): The ADC_BUF[$index] is "owned" by this peripheral. // It shall not be used anywhere else in the module. unsafe { &mut ADC_BUF[$index][0] }, - None, + unsafe { Some(&mut ADC_BUF[$index][1]) }, data_config, ); Self { - // Note(unsafe): The ADC_BUF[$index][1] is "owned" by this peripheral. It - // shall not be used anywhere else in the module. - next_buffer: unsafe { Some(&mut ADC_BUF[$index][1]) }, transfer: data_transfer, trigger_transfer, clear_transfer, @@ -364,27 +353,17 @@ macro_rules! adc_input { } - /// Obtain a buffer filled with ADC samples. + /// Wait for the transfer of the currently active buffer to complete, + /// then call a function on the now inactive buffer and acknowledge the + /// transfer complete flag. /// - /// # Returns - /// A reference to the underlying buffer that has been filled with ADC samples. - pub fn acquire_buffer(&mut self) -> &[u16; SAMPLE_BUFFER_SIZE] { - // Wait for the transfer to fully complete before continuing. Note: If a device - // hangs up, check that this conditional is passing correctly, as there is no - // time-out checks here in the interest of execution speed. - while !self.transfer.get_transfer_complete_flag() {} - - let next_buffer = self.next_buffer.take().unwrap(); - - // Start the next transfer. - self.transfer.clear_interrupts(); - let (prev_buffer, _, _) = - self.transfer.next_transfer(next_buffer).unwrap(); - - // .unwrap_none() https://github.com/rust-lang/rust/issues/62633 - self.next_buffer.replace(prev_buffer); - - self.next_buffer.as_ref().unwrap() + /// NOTE(unsafe): Memory safety and access ordering is not guaranteed + /// (see the HAL DMA docs). + pub fn with_buffer(&mut self, f: F) -> R + where + F: FnOnce(&mut SampleBuffer) -> R, + { + unsafe { self.transfer.next_dbm_transfer_with(|buf, _current| f(buf)) } } } } diff --git a/src/hardware/dac.rs b/src/hardware/dac.rs index 56f3033..91f8610 100644 --- a/src/hardware/dac.rs +++ b/src/hardware/dac.rs @@ -52,7 +52,7 @@ ///! served promptly after the transfer completes. use stm32h7xx_hal as hal; -use super::design_parameters::SAMPLE_BUFFER_SIZE; +use super::design_parameters::{SampleBuffer, SAMPLE_BUFFER_SIZE}; use super::timers; use hal::dma::{ @@ -66,8 +66,7 @@ use hal::dma::{ // processed). Note that the contents of AXI SRAM is uninitialized, so the buffer contents on // startup are undefined. The dimensions are `ADC_BUF[adc_index][ping_pong_index][sample_index]`. #[link_section = ".axisram.buffers"] -static mut DAC_BUF: [[[u16; SAMPLE_BUFFER_SIZE]; 3]; 2] = - [[[0; SAMPLE_BUFFER_SIZE]; 3]; 2]; +static mut DAC_BUF: [[SampleBuffer; 2]; 2] = [[[0; SAMPLE_BUFFER_SIZE]; 2]; 2]; /// Custom type for referencing DAC output codes. /// The internal integer is the raw code written to the DAC output register. @@ -137,13 +136,12 @@ macro_rules! dac_output { /// Represents data associated with DAC. pub struct $name { - next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>, // Note: SPI TX functionality may not be used from this structure to ensure safety with DMA. transfer: Transfer< hal::dma::dma::$data_stream, $spi, MemoryToPeripheral, - &'static mut [u16; SAMPLE_BUFFER_SIZE], + &'static mut SampleBuffer, hal::dma::DBTransfer, >, } @@ -198,33 +196,26 @@ macro_rules! dac_output { trigger_config, ); - Self { - transfer, - // Note(unsafe): This buffer is only used once and provided for the next DMA transfer. - next_buffer: unsafe { Some(&mut DAC_BUF[$index][2]) }, - } + Self { transfer } } pub fn start(&mut self) { self.transfer.start(|spi| spi.start_dma()); } - /// Acquire the next output buffer to populate it with DAC codes. - pub fn acquire_buffer(&mut self) -> &mut [u16; SAMPLE_BUFFER_SIZE] { - // Note: If a device hangs up, check that this conditional is passing correctly, as - // there is no time-out checks here in the interest of execution speed. - while !self.transfer.get_transfer_complete_flag() {} - - let next_buffer = self.next_buffer.take().unwrap(); - - // Start the next transfer. - let (prev_buffer, _, _) = - self.transfer.next_transfer(next_buffer).unwrap(); - - // .unwrap_none() https://github.com/rust-lang/rust/issues/62633 - self.next_buffer.replace(prev_buffer); - - self.next_buffer.as_mut().unwrap() + /// Wait for the transfer of the currently active buffer to complete, + /// then call a function on the now inactive buffer and acknowledge the + /// transfer complete flag. + /// + /// NOTE(unsafe): Memory safety and access ordering is not guaranteed + /// (see the HAL DMA docs). + pub fn with_buffer(&mut self, f: F) -> R + where + F: FnOnce(&mut SampleBuffer) -> R, + { + unsafe { + self.transfer.next_dbm_transfer_with(|buf, _current| f(buf)) + } } } }; diff --git a/src/hardware/design_parameters.rs b/src/hardware/design_parameters.rs index 9a4279b..d04cc75 100644 --- a/src/hardware/design_parameters.rs +++ b/src/hardware/design_parameters.rs @@ -50,5 +50,7 @@ pub const ADC_SAMPLE_TICKS: u16 = 1 << ADC_SAMPLE_TICKS_LOG2; pub const SAMPLE_BUFFER_SIZE_LOG2: u8 = 3; pub const SAMPLE_BUFFER_SIZE: usize = 1 << SAMPLE_BUFFER_SIZE_LOG2; +pub type SampleBuffer = [u16; SAMPLE_BUFFER_SIZE]; + // The MQTT broker IPv4 address pub const MQTT_BROKER: [u8; 4] = [10, 34, 16, 10]; From 2a9657f98c617b88eae2d4b8afc60842bbdfd45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 24 May 2021 22:41:22 +0200 Subject: [PATCH 04/40] dual-iir: destructure resources --- src/bin/dual-iir.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index dec0697..855e578 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -123,14 +123,24 @@ const APP: () = { /// the same time bounds, meeting one also means the other is also met. #[task(binds=DMA1_STR4, resources=[adcs, digital_inputs, dacs, iir_state, settings, telemetry], priority=2)] fn process(mut c: process::Context) { - let adc0 = &mut c.resources.adcs.0; - let adc1 = &mut c.resources.adcs.1; - let dac0 = &mut c.resources.dacs.0; - let dac1 = &mut c.resources.dacs.1; - let di = &c.resources.digital_inputs; - let settings = &c.resources.settings; - let iir_state = &mut c.resources.iir_state; - let telemetry = &mut c.resources.telemetry; + let process::Resources { + adcs: (ref mut adc0, ref mut adc1), + dacs: (ref mut dac0, ref mut dac1), + ref digital_inputs, + ref settings, + ref mut iir_state, + ref mut telemetry, + } = c.resources; + + let digital_inputs = [ + digital_inputs.0.is_high().unwrap(), + digital_inputs.1.is_high().unwrap(), + ]; + telemetry.digital_inputs = digital_inputs; + + let hold = + settings.force_hold || (digital_inputs[1] && settings.allow_hold); + adc0.with_buffer(|a0| { adc1.with_buffer(|a1| { dac0.with_buffer(|d0| { @@ -138,12 +148,6 @@ const APP: () = { let adc_samples = [a0, a1]; let dac_samples = [d0, d1]; - let digital_inputs = - [di.0.is_high().unwrap(), di.1.is_high().unwrap()]; - - let hold = settings.force_hold - || (digital_inputs[1] && settings.allow_hold); - // Preserve instruction and data ordering w.r.t. DMA flag access. fence(Ordering::SeqCst); @@ -181,8 +185,6 @@ const APP: () = { DacCode(dac_samples[1][0]), ]; - telemetry.digital_inputs = digital_inputs; - // Preserve instruction and data ordering w.r.t. DMA flag access. fence(Ordering::SeqCst); }) From c13859d486479872415d7a57315fd616aeabc099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 25 May 2021 12:05:19 +0200 Subject: [PATCH 05/40] dual-iir: add closure nesting helper macro --- src/bin/dual-iir.rs | 89 ++++++++++++++++++++++----------------------- src/lib.rs | 3 -- 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 855e578..3ce9817 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -52,6 +52,15 @@ impl Default for Settings { } } +macro_rules! flatten_closures { + ($fn:ident, $e:ident, $fun:block) => { + $e.$fn(|$e| $fun ) + }; + ($fn:ident, $e:ident, $($es:ident),+, $fun:block) => { + $e.$fn(|$e| flatten_closures!($fn, $($es),*, $fun)) + }; +} + #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] const APP: () = { struct Resources { @@ -141,55 +150,43 @@ const APP: () = { let hold = settings.force_hold || (digital_inputs[1] && settings.allow_hold); - adc0.with_buffer(|a0| { - adc1.with_buffer(|a1| { - dac0.with_buffer(|d0| { - dac1.with_buffer(|d1| { - let adc_samples = [a0, a1]; - let dac_samples = [d0, d1]; + flatten_closures!(with_buffer, adc0, adc1, dac0, dac1, { + let adc_samples = [adc0, adc1]; + let dac_samples = [dac0, dac1]; - // Preserve instruction and data ordering w.r.t. DMA flag access. - fence(Ordering::SeqCst); + // Preserve instruction and data ordering w.r.t. DMA flag access. + fence(Ordering::SeqCst); - for channel in 0..adc_samples.len() { - adc_samples[channel] - .iter() - .zip(dac_samples[channel].iter_mut()) - .map(|(ai, di)| { - let x = f32::from(*ai as i16); - let y = settings.iir_ch[channel] - .iter() - .zip(iir_state[channel].iter_mut()) - .fold(x, |yi, (ch, state)| { - ch.update(state, yi, hold) - }); - // Note(unsafe): The filter limits must ensure that - // the value is in range. - // The truncation introduces 1/2 LSB distortion. - let y: i16 = - unsafe { y.to_int_unchecked() }; - // Convert to DAC code - *di = DacCode::from(y).0; - }) - .last(); - } - - // Update telemetry measurements. - telemetry.adcs = [ - AdcCode(adc_samples[0][0]), - AdcCode(adc_samples[1][0]), - ]; - - telemetry.dacs = [ - DacCode(dac_samples[0][0]), - DacCode(dac_samples[1][0]), - ]; - - // Preserve instruction and data ordering w.r.t. DMA flag access. - fence(Ordering::SeqCst); + for channel in 0..adc_samples.len() { + adc_samples[channel] + .iter() + .zip(dac_samples[channel].iter_mut()) + .map(|(ai, di)| { + let x = f32::from(*ai as i16); + let y = settings.iir_ch[channel] + .iter() + .zip(iir_state[channel].iter_mut()) + .fold(x, |yi, (ch, state)| { + ch.update(state, yi, hold) + }); + // Note(unsafe): The filter limits must ensure that the value is in range. + // The truncation introduces 1/2 LSB distortion. + let y: i16 = unsafe { y.to_int_unchecked() }; + // Convert to DAC code + *di = DacCode::from(y).0; }) - }) - }) + .last(); + } + + // Update telemetry measurements. + telemetry.adcs = + [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])]; + + telemetry.dacs = + [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])]; + + // Preserve instruction and data ordering w.r.t. DMA flag access. + fence(Ordering::SeqCst); }); } diff --git a/src/lib.rs b/src/lib.rs index 975b08f..85964a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,5 @@ #![no_std] #![cfg_attr(feature = "nightly", feature(core_intrinsics))] -#[macro_use] -extern crate log; - pub mod hardware; pub mod net; From 316dbb3d2e8860781c703c766fed4a8c770f10cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 17:59:30 +0200 Subject: [PATCH 06/40] dual-iir: fmt --- src/bin/dual-iir.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index 65704ea..e434e66 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -14,7 +14,6 @@ use stabilizer::{ net::{Miniconf, NetworkState, NetworkUsers, Telemetry, TelemetryBuffer}, }; - const SCALE: f32 = i16::MAX as _; // The number of cascaded IIR biquads per channel. Select 1 or 2! From f1a58b781164bf2764c264feeaa9aaed1143b69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 25 May 2021 15:21:09 +0000 Subject: [PATCH 07/40] pounder: simplify attenuator spi interface --- src/hardware/pounder/attenuators.rs | 19 ++++++++----------- src/hardware/pounder/mod.rs | 21 ++------------------- 2 files changed, 10 insertions(+), 30 deletions(-) diff --git a/src/hardware/pounder/attenuators.rs b/src/hardware/pounder/attenuators.rs index 156da9f..092338e 100644 --- a/src/hardware/pounder/attenuators.rs +++ b/src/hardware/pounder/attenuators.rs @@ -30,16 +30,16 @@ pub trait AttenuatorInterface { // Read all the channels, modify the channel of interest, and write all the channels back. // This ensures the staging register and the output register are always in sync. let mut channels = [0_u8; 4]; - self.read_all_attenuators(&mut channels)?; + self.transfer_attenuators(&mut channels)?; // The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the // attenuator code into the upper 6 bits of the register value. Note that the attenuator // treats inputs as active-low, so the code is inverted before writing. channels[channel as usize] = (!attenuation_code) << 2; - self.write_all_attenuators(&channels)?; + self.transfer_attenuators(&mut channels)?; // Finally, latch the output of the updated channel to force it into an active state. - self.latch_attenuators(channel)?; + self.latch_attenuator(channel)?; Ok(attenuation_code as f32 / 2.0) } @@ -57,8 +57,8 @@ pub trait AttenuatorInterface { // Reading the data always shifts data out of the staging registers, so we perform a // duplicate write-back to ensure the staging register is always equal to the output // register. - self.read_all_attenuators(&mut channels)?; - self.write_all_attenuators(&channels)?; + self.transfer_attenuators(&mut channels)?; + self.transfer_attenuators(&mut channels)?; // The attenuation code is stored in the upper 6 bits of the register, where each LSB // represents 0.5 dB. The attenuator stores the code as active-low, so inverting the result @@ -74,13 +74,10 @@ pub trait AttenuatorInterface { fn reset_attenuators(&mut self) -> Result<(), Error>; - fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error>; - fn read_all_attenuators( + fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error>; + + fn transfer_attenuators( &mut self, channels: &mut [u8; 4], ) -> Result<(), Error>; - fn write_all_attenuators( - &mut self, - channels: &[u8; 4], - ) -> Result<(), Error>; } diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 32a8e80..dd5ce95 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -344,7 +344,7 @@ impl AttenuatorInterface for PounderDevices { /// /// Args: /// * `channel` - The attenuator channel to latch. - fn latch_attenuators(&mut self, channel: Channel) -> Result<(), Error> { + fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { Channel::In0 => ATT_LE0_PIN, Channel::In1 => ATT_LE2_PIN, @@ -368,7 +368,7 @@ impl AttenuatorInterface for PounderDevices { /// /// Args: /// * `channels` - A slice to store the channel readings into. - fn read_all_attenuators( + fn transfer_attenuators( &mut self, channels: &mut [u8; 4], ) -> Result<(), Error> { @@ -378,23 +378,6 @@ impl AttenuatorInterface for PounderDevices { Ok(()) } - - /// Write the attenuator shift registers. - /// - /// Args: - /// * `channels` - The data to write into the attenuators. - fn write_all_attenuators( - &mut self, - channels: &[u8; 4], - ) -> Result<(), Error> { - let mut result = [0_u8; 4]; - result.clone_from_slice(channels); - self.attenuator_spi - .transfer(&mut result) - .map_err(|_| Error::Spi)?; - - Ok(()) - } } impl PowerMeasurementInterface for PounderDevices { From a9f5943296b7d324b75d43f2735f4e41be0189ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 25 May 2021 20:59:32 +0000 Subject: [PATCH 08/40] deps: use mcp23017 release --- Cargo.lock | 5 +++-- Cargo.toml | 6 +----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5791b3a..196b050 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -404,8 +404,9 @@ dependencies = [ [[package]] name = "mcp23017" -version = "0.1.1" -source = "git+https://github.com/lucazulian/mcp23017.git?rev=523d71d#523d71dcb11fc0ea4bd9385ef2527ae7a7eee987" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c32fd6627e73f1cfa95c00ddcdcb5a6a6ddbd10b308d08588a502c018b6e12c" dependencies = [ "embedded-hal", ] diff --git a/Cargo.toml b/Cargo.toml index 2eb2430..0ecfe66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,17 +45,13 @@ ad9959 = { path = "ad9959" } miniconf = "0.1.0" shared-bus = {version = "0.2.2", features = ["cortex-m"] } serde-json-core = "0.3" +mcp23017 = "1.0" # rtt-target bump [dependencies.rtt-logger] git = "https://github.com/quartiq/rtt-logger.git" rev = "70b0eb5" -# rewrite -[dependencies.mcp23017] -git = "https://github.com/lucazulian/mcp23017.git" -rev = "523d71d" - [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] version = "0.9.0" From fc78b087eeba5343d25dd90f729e4bf1a5180dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 25 May 2021 20:59:48 +0000 Subject: [PATCH 09/40] attenuators: use robust latching sequence To ensure that eatch call to latch() generates a rising edge, first assert low, then high, not the other way round. --- src/hardware/pounder/mod.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index dd5ce95..e7f072e 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -313,12 +313,7 @@ impl PounderDevices { .map_err(|_| Error::I2c)?; devices .mcp23017 - .write_gpio(mcp23017::Port::GPIOB, 1 << 5) - .map_err(|_| Error::I2c)?; - - devices - .mcp23017 - .digital_write(EXT_CLK_SEL_PIN, false) + .write_gpio(mcp23017::Port::GPIOB, 0x2F) .map_err(|_| Error::I2c)?; Ok(devices) @@ -351,23 +346,25 @@ impl AttenuatorInterface for PounderDevices { Channel::Out0 => ATT_LE1_PIN, Channel::Out1 => ATT_LE3_PIN, }; - self.mcp23017 - .digital_write(pin, true) + .digital_write(pin, false) + .map_err(|_| Error::I2c)?; + self.mcp23017 + .digital_write(pin, false) .map_err(|_| Error::I2c)?; // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is // sufficient. Document the delay here. self.mcp23017 - .digital_write(pin, false) + .digital_write(pin, true) .map_err(|_| Error::I2c)?; - Ok(()) } /// Read the raw attenuation codes stored in the attenuator shift registers. /// /// Args: - /// * `channels` - A slice to store the channel readings into. + /// * `channels` - A 4 byte slice to be shifted into the + /// attenuators and to contain the data shifted out. fn transfer_attenuators( &mut self, channels: &mut [u8; 4], From 79f883810433a55427791abb76a7163cadac07d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 14:09:26 +0000 Subject: [PATCH 10/40] rf_power: fix measurement --- src/hardware/pounder/rf_power.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/hardware/pounder/rf_power.rs b/src/hardware/pounder/rf_power.rs index ac82b5d..977bd64 100644 --- a/src/hardware/pounder/rf_power.rs +++ b/src/hardware/pounder/rf_power.rs @@ -1,20 +1,22 @@ use super::{Channel, Error}; -/// Provide an interface to measure RF input power in dB. +/// Provide an interface to measure RF input power in dBm. pub trait PowerMeasurementInterface { fn sample_converter(&mut self, channel: Channel) -> Result; /// Measure the power of an input channel in dBm. /// - /// Note: This function assumes the input channel is connected to an AD8363 output. - /// /// Args: - /// * `channel` - The pounder channel to measure the power of in dBm. + /// * `channel` - The pounder input channel to measure the power of. + /// + /// Returns: + /// Power in dBm after the digitally controlled attenuator before the amplifier. fn measure_power(&mut self, channel: Channel) -> Result { let analog_measurement = self.sample_converter(channel)?; - // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7mV / dB at - // 100MHz. It also indicates a y-intercept of -58dBm. - Ok(analog_measurement / 0.0517 - 58.0) + // The AD8363 with VSET connected to VOUT provides an output voltage of 51.7 mV/dB at + // 100MHz with an intercept of -58 dBm. + // It is placed behind a 20 dB tap. + Ok(analog_measurement * (1. / 0.0517) + (-58. + 20.)) } } From 94f60c100c7396bba9adab1d98d0ba18aa3a97b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 14:18:46 +0000 Subject: [PATCH 11/40] pounder io extender: hack around some bug --- src/hardware/pounder/mod.rs | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index e7f072e..2e95141 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -17,6 +17,7 @@ use rf_power::PowerMeasurementInterface; use embedded_hal::{adc::OneShot, blocking::spi::Transfer}; +#[allow(dead_code)] const EXT_CLK_SEL_PIN: u8 = 8 + 7; #[allow(dead_code)] const OSC_EN_N_PIN: u8 = 8 + 6; @@ -300,16 +301,16 @@ impl PounderDevices { adc2_in_p, }; - // Configure power-on-default state for pounder. All LEDs are on, on-board oscillator - // selected, attenuators out of reset. Note that testing indicates the output state needs to - // be set first to properly update the output registers. + // Configure power-on-default state for pounder. All LEDs are off, on-board oscillator + // selected and enabled, attenuators out of reset. Note that testing indicates the + // output state needs to be set first to properly update the output registers. devices .mcp23017 .all_pin_mode(mcp23017::PinMode::OUTPUT) .map_err(|_| Error::I2c)?; devices .mcp23017 - .write_gpio(mcp23017::Port::GPIOA, 0x3F) + .write_gpio(mcp23017::Port::GPIOA, 0x00) .map_err(|_| Error::I2c)?; devices .mcp23017 @@ -324,12 +325,11 @@ impl AttenuatorInterface for PounderDevices { /// Reset all of the attenuators to a power-on default state. fn reset_attenuators(&mut self) -> Result<(), Error> { self.mcp23017 - .digital_write(ATT_RST_N_PIN, false) + .write_gpio(mcp23017::Port::GPIOB, 0x0f) .map_err(|_| Error::I2c)?; - // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is - // sufficient. Document the delay here. + // Duration of one I2C transaction is sufficiently long. self.mcp23017 - .digital_write(ATT_RST_N_PIN, true) + .write_gpio(mcp23017::Port::GPIOB, 0x2f) .map_err(|_| Error::I2c)?; Ok(()) @@ -341,21 +341,17 @@ impl AttenuatorInterface for PounderDevices { /// * `channel` - The attenuator channel to latch. fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error> { let pin = match channel { - Channel::In0 => ATT_LE0_PIN, - Channel::In1 => ATT_LE2_PIN, - Channel::Out0 => ATT_LE1_PIN, - Channel::Out1 => ATT_LE3_PIN, + Channel::In0 => 0, + Channel::Out0 => 1, + Channel::In1 => 2, + Channel::Out1 => 3, }; self.mcp23017 - .digital_write(pin, false) + .write_gpio(mcp23017::Port::GPIOB, 0x2f & !(1 << pin)) .map_err(|_| Error::I2c)?; + // Duration of one I2C transaction is sufficiently long. self.mcp23017 - .digital_write(pin, false) - .map_err(|_| Error::I2c)?; - // TODO: Measure the I2C transaction speed to the RST pin to ensure that the delay is - // sufficient. Document the delay here. - self.mcp23017 - .digital_write(pin, true) + .write_gpio(mcp23017::Port::GPIOB, 0x2f) .map_err(|_| Error::I2c)?; Ok(()) } From 3616f1fa5aacf8a2376f85c9a96c7a251df957f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 14:53:32 +0000 Subject: [PATCH 12/40] pounder: fix attenuator indices (latch and shiftreg) Before the discriminant was used despite being a compiler implementaiton detail. This now fixes the discriminant to match byte index in the attenuator shift register and latch-enable index of the gpio extender. --- src/hardware/pounder/attenuators.rs | 2 +- src/hardware/pounder/mod.rs | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/hardware/pounder/attenuators.rs b/src/hardware/pounder/attenuators.rs index 092338e..ef22814 100644 --- a/src/hardware/pounder/attenuators.rs +++ b/src/hardware/pounder/attenuators.rs @@ -35,7 +35,7 @@ pub trait AttenuatorInterface { // The lowest 2 bits of the 8-bit shift register on the attenuator are ignored. Shift the // attenuator code into the upper 6 bits of the register value. Note that the attenuator // treats inputs as active-low, so the code is inverted before writing. - channels[channel as usize] = (!attenuation_code) << 2; + channels[channel as usize] = !(attenuation_code << 2); self.transfer_attenuators(&mut channels)?; // Finally, latch the output of the updated channel to force it into an active state. diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 2e95141..9e4c228 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -41,10 +41,10 @@ pub enum Error { #[derive(Debug, Copy, Clone)] #[allow(dead_code)] pub enum Channel { - In0, - In1, - Out0, - Out1, + In0 = 0, + Out0 = 1, + In1 = 2, + Out1 = 3, } #[derive(Serialize, Deserialize, Copy, Clone, Debug)] @@ -340,12 +340,7 @@ impl AttenuatorInterface for PounderDevices { /// Args: /// * `channel` - The attenuator channel to latch. fn latch_attenuator(&mut self, channel: Channel) -> Result<(), Error> { - let pin = match channel { - Channel::In0 => 0, - Channel::Out0 => 1, - Channel::In1 => 2, - Channel::Out1 => 3, - }; + let pin = channel as u8; self.mcp23017 .write_gpio(mcp23017::Port::GPIOB, 0x2f & !(1 << pin)) .map_err(|_| Error::I2c)?; From 2368a4f6edfe12c2673c03e439c0345e886df2c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 15:30:11 +0000 Subject: [PATCH 13/40] pounder: enum for gpio ext pins This is currently unused but it's better to have enums than a set of assorted constants. --- src/hardware/configuration.rs | 2 +- src/hardware/pounder/mod.rs | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index e2eb653..d74b729 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -802,7 +802,7 @@ pub fn setup( let scl = gpiob.pb8.into_alternate_af4().set_open_drain(); let i2c1 = device.I2C1.i2c( (scl, sda), - 100.khz(), + 400.khz(), ccdr.peripheral.I2C1, &ccdr.clocks, ); diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 9e4c228..7257217 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -17,15 +17,21 @@ use rf_power::PowerMeasurementInterface; use embedded_hal::{adc::OneShot, blocking::spi::Transfer}; -#[allow(dead_code)] -const EXT_CLK_SEL_PIN: u8 = 8 + 7; -#[allow(dead_code)] -const OSC_EN_N_PIN: u8 = 8 + 6; -const ATT_RST_N_PIN: u8 = 8 + 5; -const ATT_LE3_PIN: u8 = 8 + 3; -const ATT_LE2_PIN: u8 = 8 + 2; -const ATT_LE1_PIN: u8 = 8 + 1; -const ATT_LE0_PIN: u8 = 8; +pub enum GpioPin { + Led4Green = 0, + Led5Red = 1, + Led6Green = 2, + Led7Red = 3, + Led8Green = 4, + Led9Red = 5, + AttLe0 = 8 + 0, + AttLe1 = 8 + 1, + AttLe2 = 8 + 2, + AttLe3 = 8 + 3, + AttRstN = 8 + 5, + OscEnN = 8 + 6, + ExtClkSel = 8 + 7, +} #[derive(Debug, Copy, Clone)] pub enum Error { From 4f9113cb45b189791e134e20d0893a1121aaf8ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Wed, 26 May 2021 16:06:55 +0000 Subject: [PATCH 14/40] ad9959: refactor pad() --- ad9959/src/lib.rs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 21dfa21..4a66349 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -596,6 +596,22 @@ impl ProfileSerializer { self.index += value.len() + 1; } + 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!(), + } + } + /// Get the serialized profile as a slice of 32-bit words. /// /// # Note @@ -604,21 +620,8 @@ impl ProfileSerializer { /// /// # Returns /// A slice of `u32` words representing the serialized profile. - pub fn finalize<'a>(&'a mut self) -> &[u32] { - // Pad the buffer to 32-bit alignment by adding dummy writes to CSR and LSRR. - let padding = 4 - (self.index % 4); - match padding { - 1 => { - // For a pad size of 1, 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]), - 3 => self.add_write(Register::LSRR, &[0, 0]), - 4 => {} - - _ => unreachable!(), - } + pub fn finalize<'a>(&'a mut self) -> &'a [u32] { + self.pad(); unsafe { core::slice::from_raw_parts::<'a, u32>( &self.data as *const _ as *const u32, From 21ab98823985127d97bdda25c377880ee1e0a051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 6 May 2021 10:10:59 +0200 Subject: [PATCH 15/40] atan2: tweak for speed --- dsp/src/atan2.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dsp/src/atan2.rs b/dsp/src/atan2.rs index 6d6e476..bfe33db 100644 --- a/dsp/src/atan2.rs +++ b/dsp/src/atan2.rs @@ -77,6 +77,8 @@ pub fn atan2(y: i32, x: i32) -> i32 { if sign.1 { angle = angle.wrapping_neg(); + // Negation ends up in slightly faster assembly + // angle = !angle; } angle From 2ba49258f98c6bde25d20020ea5f3ebfb768dffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Sat, 29 May 2021 22:50:37 +0200 Subject: [PATCH 16/40] dsp/staurating_scale: fix math --- dsp/src/tools.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/dsp/src/tools.rs b/dsp/src/tools.rs index 6fc2b12..2337159 100644 --- a/dsp/src/tools.rs +++ b/dsp/src/tools.rs @@ -91,16 +91,12 @@ pub fn macc_i32(y0: i32, x: &[i32], a: &[i32], shift: u32) -> i32 { /// Combine high and low i32 into a single downscaled i32, saturating the type. pub fn saturating_scale(lo: i32, hi: i32, shift: u32) -> i32 { debug_assert!(shift & 31 == shift); - - let shift_hi = 31 - shift; - debug_assert!(shift_hi & 31 == shift_hi); - - let over = hi >> shift; - if over < -1 { - i32::MIN - } else if over > 0 { + let scale = -1 << shift; + if hi <= scale { + -i32::MAX + } else if -hi <= scale { i32::MAX } else { - (lo >> shift) + (hi << shift_hi) + (lo >> shift) + (hi << (31 - shift)) } } From fb4ed888ee83a50ead941374c8a78466f5f8a559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Sun, 30 May 2021 20:12:02 +0200 Subject: [PATCH 17/40] dsp/saturating_scale: fix range --- dsp/src/tools.rs | 13 ------------- dsp/src/unwrap.rs | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/dsp/src/tools.rs b/dsp/src/tools.rs index 2337159..9fba214 100644 --- a/dsp/src/tools.rs +++ b/dsp/src/tools.rs @@ -87,16 +87,3 @@ pub fn macc_i32(y0: i32, x: &[i32], a: &[i32], shift: u32) -> i32 { .fold(y0, |y, xa| y + xa); (y >> shift) as i32 } - -/// Combine high and low i32 into a single downscaled i32, saturating the type. -pub fn saturating_scale(lo: i32, hi: i32, shift: u32) -> i32 { - debug_assert!(shift & 31 == shift); - let scale = -1 << shift; - if hi <= scale { - -i32::MAX - } else if -hi <= scale { - i32::MAX - } else { - (lo >> shift) + (hi << (31 - shift)) - } -} diff --git a/dsp/src/unwrap.rs b/dsp/src/unwrap.rs index 870629e..68ddd0c 100644 --- a/dsp/src/unwrap.rs +++ b/dsp/src/unwrap.rs @@ -16,6 +16,27 @@ pub fn overflowing_sub(y: i32, x: i32) -> (i32, i8) { (delta, wrap) } +/// Combine high and low i32 into a single downscaled i32, saturating monotonically. +/// +/// Args: +/// `lo`: LSB i32 to scale down by `shift` and range-extend with `hi` +/// `hi`: MSB i32 to scale up and extend `lo` with. Output will be clipped if +/// `hi` exceeds the output i32 range. +/// `shift`: Downscale `lo` by that many bits. Values from 1 to 32 inclusive +/// are valid. +pub fn saturating_scale(lo: i32, hi: i32, shift: u32) -> i32 { + debug_assert!(shift > 0); + debug_assert!(shift <= 32); + let hi_range = -1 << (shift - 1); + if hi <= hi_range { + i32::MIN - hi_range + } else if -hi <= hi_range { + hi_range - i32::MIN + } else { + (lo >> shift) + (hi << (32 - shift)) + } +} + /// Overflow unwrapper. /// /// This is unwrapping as in the phase and overflow unwrapping context, not From da7fc08c15de77362312926052fb8d3c5076ff4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 31 May 2021 18:25:34 +0200 Subject: [PATCH 18/40] pounder: add comment on channel enum --- src/hardware/pounder/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 7257217..3f4cd3d 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -44,6 +44,8 @@ pub enum Error { Adc, } +/// The numerical value (discriminant) of the Channel enum is the index in the attenuator shift +/// register as well as the attenuator latch enable signal index on the GPIO extender. #[derive(Debug, Copy, Clone)] #[allow(dead_code)] pub enum Channel { From 4c01a8f92925d8657ddeef76f5bc8fa2815d08f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 31 May 2021 20:41:18 +0200 Subject: [PATCH 19/40] pounder: clippy --- ad9959/src/lib.rs | 1 + src/hardware/pounder/mod.rs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/ad9959/src/lib.rs b/ad9959/src/lib.rs index 4a66349..cafe4fe 100644 --- a/ad9959/src/lib.rs +++ b/ad9959/src/lib.rs @@ -43,6 +43,7 @@ pub enum Mode { /// The configuration registers within the AD9959 DDS device. The values of each register are /// equivalent to the address. +#[allow(clippy::upper_case_acronyms)] pub enum Register { CSR = 0x00, FR1 = 0x01, diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index 3f4cd3d..3105632 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -24,7 +24,7 @@ pub enum GpioPin { Led7Red = 3, Led8Green = 4, Led9Red = 5, - AttLe0 = 8 + 0, + AttLe0 = 8, AttLe1 = 8 + 1, AttLe2 = 8 + 2, AttLe3 = 8 + 3, @@ -89,14 +89,14 @@ pub struct DdsClockConfig { pub external_clock: bool, } -impl Into for Channel { +impl From for ad9959::Channel { /// Translate pounder channels to DDS output channels. - fn into(self) -> ad9959::Channel { - match self { - Channel::In0 => ad9959::Channel::Two, - Channel::In1 => ad9959::Channel::Four, - Channel::Out0 => ad9959::Channel::One, - Channel::Out1 => ad9959::Channel::Three, + fn from(other: Channel) -> Self { + match other { + Channel::In0 => Self::Two, + Channel::In1 => Self::Four, + Channel::Out0 => Self::One, + Channel::Out1 => Self::Three, } } } From c5a2704c41ec5f7089c705effe5af14d1a064cf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 13:11:16 +0200 Subject: [PATCH 20/40] dma: implement overflow checking --- Cargo.lock | 2 +- Cargo.toml | 3 ++- src/bin/dual-iir.rs | 4 ++-- src/hardware/adc.rs | 4 ++-- src/hardware/dac.rs | 4 ++-- src/hardware/pounder/dds_output.rs | 4 +++- src/net/miniconf_client.rs | 1 + 7 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fbbe91..de1f22f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -810,7 +810,7 @@ dependencies = [ [[package]] name = "stm32h7xx-hal" version = "0.9.0" -source = "git+https://github.com/quartiq/stm32h7xx-hal.git?rev=cca4ecc#cca4ecc3e0cc8cb2f7a9652c4099d50b44977493" +source = "git+https://github.com/quartiq/stm32h7xx-hal.git?rev=b0b8a93#b0b8a930b2c3bc5fcebc2e905b4c5e13360111a5" dependencies = [ "bare-metal 1.0.0", "cast", diff --git a/Cargo.toml b/Cargo.toml index 93eaa9a..99b390e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,11 +52,12 @@ mcp23017 = "1.0" git = "https://github.com/quartiq/rtt-logger.git" rev = "70b0eb5" +# fast double buffered DMA without poisoning and buffer swapping [dependencies.stm32h7xx-hal] features = ["stm32h743v", "rt", "unproven", "ethernet", "quadspi"] # version = "0.9.0" git = "https://github.com/quartiq/stm32h7xx-hal.git" -rev = "cca4ecc" +rev = "b0b8a93" # link.x section start/end [patch.crates-io.cortex-m-rt] diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index a45d603..ab965ee 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -54,10 +54,10 @@ impl Default for Settings { macro_rules! flatten_closures { ($fn:ident, $e:ident, $fun:block) => { - $e.$fn(|$e| $fun ) + $e.$fn(|$e| $fun ).unwrap() }; ($fn:ident, $e:ident, $($es:ident),+, $fun:block) => { - $e.$fn(|$e| flatten_closures!($fn, $($es),*, $fun)) + $e.$fn(|$e| flatten_closures!($fn, $($es),*, $fun)).unwrap() }; } diff --git a/src/hardware/adc.rs b/src/hardware/adc.rs index 101d762..8919479 100644 --- a/src/hardware/adc.rs +++ b/src/hardware/adc.rs @@ -74,7 +74,7 @@ use hal::dma::{ config::Priority, dma::{DMAReq, DmaConfig}, traits::TargetAddress, - MemoryToPeripheral, PeripheralToMemory, Transfer, + DMAError, MemoryToPeripheral, PeripheralToMemory, Transfer, }; /// A type representing an ADC sample. @@ -359,7 +359,7 @@ macro_rules! adc_input { /// /// NOTE(unsafe): Memory safety and access ordering is not guaranteed /// (see the HAL DMA docs). - pub fn with_buffer(&mut self, f: F) -> R + pub fn with_buffer(&mut self, f: F) -> Result where F: FnOnce(&mut SampleBuffer) -> R, { diff --git a/src/hardware/dac.rs b/src/hardware/dac.rs index 91f8610..013c43a 100644 --- a/src/hardware/dac.rs +++ b/src/hardware/dac.rs @@ -58,7 +58,7 @@ use super::timers; use hal::dma::{ dma::{DMAReq, DmaConfig}, traits::TargetAddress, - MemoryToPeripheral, Transfer, + DMAError, MemoryToPeripheral, Transfer, }; // The following global buffers are used for the DAC code DMA transfers. Two buffers are used for @@ -209,7 +209,7 @@ macro_rules! dac_output { /// /// NOTE(unsafe): Memory safety and access ordering is not guaranteed /// (see the HAL DMA docs). - pub fn with_buffer(&mut self, f: F) -> R + pub fn with_buffer(&mut self, f: F) -> Result where F: FnOnce(&mut SampleBuffer) -> R, { diff --git a/src/hardware/pounder/dds_output.rs b/src/hardware/pounder/dds_output.rs index e755482..bf7acc0 100644 --- a/src/hardware/pounder/dds_output.rs +++ b/src/hardware/pounder/dds_output.rs @@ -52,9 +52,11 @@ ///! compile-time-known register update sequence needed for the application, the serialization ///! process can be done once and then register values can be written into a pre-computed serialized ///! buffer to avoid the software overhead of much of the serialization process. +use log::warn; +use stm32h7xx_hal as hal; + use super::{hrtimer::HighResTimerE, QspiInterface}; use ad9959::{Channel, DdsConfig, ProfileSerializer}; -use stm32h7xx_hal as hal; /// The DDS profile update stream. pub struct DdsOutput { diff --git a/src/net/miniconf_client.rs b/src/net/miniconf_client.rs index cc838f6..e5f328a 100644 --- a/src/net/miniconf_client.rs +++ b/src/net/miniconf_client.rs @@ -11,6 +11,7 @@ ///! Respones to settings updates are sent without quality-of-service guarantees, so there's no ///! guarantee that the requestee will be informed that settings have been applied. use heapless::String; +use log::info; use super::{MqttMessage, NetworkReference, SettingsResponse, UpdateState}; use crate::hardware::design_parameters::MQTT_BROKER; From b90f4ad18563f1577d7c383be255e9ebfa3b4277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 13:17:40 +0200 Subject: [PATCH 21/40] lockin: port to fast double buffered DMA --- src/bin/lockin.rs | 111 +++++++++++++++++++++++++--------------------- 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index 4864760..54ce37b 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -78,6 +78,15 @@ impl Default for Settings { } } +macro_rules! flatten_closures { + ($fn:ident, $e:ident, $fun:block) => { + $e.$fn(|$e| $fun ).unwrap() + }; + ($fn:ident, $e:ident, $($es:ident),+, $fun:block) => { + $e.$fn(|$e| flatten_closures!($fn, $($es),*, $fun)).unwrap() + }; +} + #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] const APP: () = { struct Resources { @@ -159,26 +168,22 @@ const APP: () = { #[task(binds=DMA1_STR4, resources=[adcs, dacs, lockin, timestamper, pll, settings, telemetry], priority=2)] #[inline(never)] #[link_section = ".itcm.process"] - fn process(c: process::Context) { - let adc_samples = [ - c.resources.adcs.0.acquire_buffer(), - c.resources.adcs.1.acquire_buffer(), - ]; - - let mut dac_samples = [ - c.resources.dacs.0.acquire_buffer(), - c.resources.dacs.1.acquire_buffer(), - ]; - - let lockin = c.resources.lockin; - let settings = c.resources.settings; + fn process(mut c: process::Context) { + let process::Resources { + adcs: (ref mut adc0, ref mut adc1), + dacs: (ref mut dac0, ref mut dac1), + ref settings, + ref mut telemetry, + ref mut lockin, + ref mut pll, + ref mut timestamper, + } = c.resources; let (reference_phase, reference_frequency) = match settings.lockin_mode { LockinMode::External => { - let timestamp = - c.resources.timestamper.latest_timestamp().unwrap_or(None); // Ignore data from timer capture overflows. - let (pll_phase, pll_frequency) = c.resources.pll.update( + let timestamp = timestamper.latest_timestamp().unwrap_or(None); // Ignore data from timer capture overflows. + let (pll_phase, pll_frequency) = pll.update( timestamp.map(|t| t as i32), settings.pll_tc[0], settings.pll_tc[1], @@ -205,45 +210,49 @@ const APP: () = { reference_phase.wrapping_mul(settings.lockin_harmonic), ); - let output: Complex = adc_samples[0] - .iter() - // Zip in the LO phase. - .zip(Accu::new(sample_phase, sample_frequency)) - // Convert to signed, MSB align the ADC sample, update the Lockin (demodulate, filter) - .map(|(&sample, phase)| { - let s = (sample as i16 as i32) << 16; - lockin.update(s, phase, settings.lockin_tc) - }) - // Decimate - .last() - .unwrap() - * 2; // Full scale assuming the 2f component is gone. + flatten_closures!(with_buffer, adc0, adc1, dac0, dac1, { + let adc_samples = [adc0, adc1]; + let mut dac_samples = [dac0, dac1]; - // Convert to DAC data. - for (channel, samples) in dac_samples.iter_mut().enumerate() { - for (i, sample) in samples.iter_mut().enumerate() { - let value = match settings.output_conf[channel] { - Conf::Magnitude => output.abs_sqr() as i32 >> 16, - Conf::Phase => output.arg() >> 16, - Conf::LogPower => (output.log2() << 24) as i32 >> 16, - Conf::ReferenceFrequency => { - reference_frequency as i32 >> 16 - } - Conf::InPhase => output.re >> 16, - Conf::Quadrature => output.im >> 16, - Conf::Modulation => DAC_SEQUENCE[i] as i32, - }; + let output: Complex = adc_samples[0] + .iter() + // Zip in the LO phase. + .zip(Accu::new(sample_phase, sample_frequency)) + // Convert to signed, MSB align the ADC sample, update the Lockin (demodulate, filter) + .map(|(&sample, phase)| { + let s = (sample as i16 as i32) << 16; + lockin.update(s, phase, settings.lockin_tc) + }) + // Decimate + .last() + .unwrap() + * 2; // Full scale assuming the 2f component is gone. - *sample = DacCode::from(value as i16).0; + // Convert to DAC data. + for (channel, samples) in dac_samples.iter_mut().enumerate() { + for (i, sample) in samples.iter_mut().enumerate() { + let value = match settings.output_conf[channel] { + Conf::Magnitude => output.abs_sqr() as i32 >> 16, + Conf::Phase => output.arg() >> 16, + Conf::LogPower => (output.log2() << 24) as i32 >> 16, + Conf::ReferenceFrequency => { + reference_frequency as i32 >> 16 + } + Conf::InPhase => output.re >> 16, + Conf::Quadrature => output.im >> 16, + Conf::Modulation => DAC_SEQUENCE[i] as i32, + }; + + *sample = DacCode::from(value as i16).0; + } } - } + // Update telemetry measurements. + telemetry.adcs = + [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])]; - // Update telemetry measurements. - c.resources.telemetry.adcs = - [AdcCode(adc_samples[0][0]), AdcCode(adc_samples[1][0])]; - - c.resources.telemetry.dacs = - [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])]; + telemetry.dacs = + [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])]; + }); } #[idle(resources=[network], spawn=[settings_update])] From f8fa297b20f0bb4f1948972ce13e926f1ddae650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 14:49:51 +0200 Subject: [PATCH 22/40] lockin: dma fence --- src/bin/lockin.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index 54ce37b..dd20278 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -2,6 +2,8 @@ #![no_std] #![no_main] +use core::sync::atomic::{fence, Ordering}; + use embedded_hal::digital::v2::InputPin; use serde::Deserialize; @@ -214,6 +216,9 @@ const APP: () = { let adc_samples = [adc0, adc1]; let mut dac_samples = [dac0, dac1]; + // Preserve instruction and data ordering w.r.t. DMA flag access. + fence(Ordering::SeqCst); + let output: Complex = adc_samples[0] .iter() // Zip in the LO phase. @@ -252,6 +257,9 @@ const APP: () = { telemetry.dacs = [DacCode(dac_samples[0][0]), DacCode(dac_samples[1][0])]; + + // Preserve instruction and data ordering w.r.t. DMA flag access. + fence(Ordering::SeqCst); }); } From 26b261364f7200d3aabe4fcc7689d166b966ef69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 14:19:24 +0000 Subject: [PATCH 23/40] pounder timestmper: don't use DMA * One sample per batch is typical and sufficient. * DMA has more overhead than direct read for one sample. --- src/hardware/configuration.rs | 6 --- src/hardware/pounder/timestamp.rs | 74 +++++-------------------------- 2 files changed, 10 insertions(+), 70 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index fca38e1..7d8c92b 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -233,11 +233,6 @@ pub fn setup( let dma_streams = hal::dma::dma::StreamsTuple::new(device.DMA1, ccdr.peripheral.DMA1); - // Early, before the DMA1 peripherals (#272) - #[cfg(feature = "pounder_v1_1")] - let dma2_streams = - hal::dma::dma::StreamsTuple::new(device.DMA2, ccdr.peripheral.DMA2); - // Configure timer 2 to trigger conversions for the ADC let mut sampling_timer = { // The timer frequency is manually adjusted below, so the 1KHz setting here is a @@ -946,7 +941,6 @@ pub fn setup( pounder::timestamp::Timestamper::new( timestamp_timer, - dma2_streams.0, tim8_channels.ch1, &mut sampling_timer, etr_pin, diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 0c06192..1e8f862 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -24,27 +24,12 @@ ///! schedule. use stm32h7xx_hal as hal; -use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer}; - -use crate::hardware::{design_parameters::SAMPLE_BUFFER_SIZE, timers}; - -// Three buffers are required for double buffered mode - 2 are owned by the DMA stream and 1 is the -// working data provided to the application. These buffers must exist in a DMA-accessible memory -// region. Note that AXISRAM is not initialized on boot, so their initial contents are undefined. -#[link_section = ".axisram.buffers"] -static mut BUF: [[u16; SAMPLE_BUFFER_SIZE]; 3] = [[0; SAMPLE_BUFFER_SIZE]; 3]; +use crate::hardware::timers; /// Software unit to timestamp stabilizer ADC samples using an external pounder reference clock. pub struct Timestamper { - next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>, timer: timers::PounderTimestampTimer, - transfer: Transfer< - hal::dma::dma::Stream0, - timers::tim8::Channel1InputCapture, - PeripheralToMemory, - &'static mut [u16; SAMPLE_BUFFER_SIZE], - hal::dma::DBTransfer, - >, + capture_channel: timers::tim8::Channel1InputCapture, } impl Timestamper { @@ -65,18 +50,12 @@ impl Timestamper { /// The new pounder timestamper in an operational state. pub fn new( mut timestamp_timer: timers::PounderTimestampTimer, - stream: hal::dma::dma::Stream0, capture_channel: timers::tim8::Channel1, sampling_timer: &mut timers::SamplingTimer, _clock_input: hal::gpio::gpioa::PA0< hal::gpio::Alternate, >, ) -> Self { - let config = DmaConfig::default() - .memory_increment(true) - .circular_buffer(true) - .double_buffer(true); - // The sampling timer should generate a trigger output when CH1 comparison occurs. sampling_timer.generate_trigger(timers::TriggerGenerator::ComparePulse); @@ -87,62 +66,29 @@ impl Timestamper { // The capture channel should capture whenever the trigger input occurs. let input_capture = capture_channel .into_input_capture(timers::tim8::CaptureSource1::TRC); - input_capture.listen_dma(); - - // The data transfer is always a transfer of data from the peripheral to a RAM buffer. - let data_transfer: Transfer<_, _, PeripheralToMemory, _, _> = - Transfer::init( - stream, - input_capture, - // Note(unsafe): BUF[0] and BUF[1] are "owned" by this peripheral. - // They shall not be used anywhere else in the module. - unsafe { &mut BUF[0] }, - unsafe { Some(&mut BUF[1]) }, - config, - ); Self { timer: timestamp_timer, - transfer: data_transfer, - - // Note(unsafe): BUF[2] is "owned" by this peripheral. It shall not be used anywhere - // else in the module. - next_buffer: unsafe { Some(&mut BUF[2]) }, + capture_channel: input_capture, } } - /// Start the DMA transfer for collecting timestamps. - #[allow(dead_code)] + /// Start collecting timestamps. pub fn start(&mut self) { - self.transfer - .start(|capture_channel| capture_channel.enable()); + self.capture_channel.enable(); } /// Update the period of the underlying timestamp timer. - #[allow(dead_code)] pub fn update_period(&mut self, period: u16) { self.timer.set_period_ticks(period); } - /// Obtain a buffer filled with timestamps. + /// Obtain a timestamp. /// /// # Returns - /// A reference to the underlying buffer that has been filled with timestamps. - #[allow(dead_code)] - pub fn acquire_buffer(&mut self) -> &[u16; SAMPLE_BUFFER_SIZE] { - // Wait for the transfer to fully complete before continuing. - // Note: If a device hangs up, check that this conditional is passing correctly, as there is - // no time-out checks here in the interest of execution speed. - while !self.transfer.get_transfer_complete_flag() {} - - let next_buffer = self.next_buffer.take().unwrap(); - - // Start the next transfer. - let (prev_buffer, _, _) = - self.transfer.next_transfer(next_buffer).unwrap(); - - self.next_buffer.replace(prev_buffer); // .unwrap_none() https://github.com/rust-lang/rust/issues/62633 - - self.next_buffer.as_ref().unwrap() + /// A `Result` potentially indicating capture overflow and containing a `Option` of a captured + /// timestamp. + pub fn latest_timestamp(&mut self) -> Result, Option> { + self.capture_channel.latest_capture() } } From d97ee3f0c427b4f7b9c5af458d15fb9be7dc6653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 16:57:51 +0200 Subject: [PATCH 24/40] Revert "pounder timestmper: don't use DMA" This reverts commit 26b261364f7200d3aabe4fcc7689d166b966ef69. First needs to reduce capture rate to batch interval. Otherwise it's jittery due to polling alignment. --- src/hardware/configuration.rs | 6 +++ src/hardware/pounder/timestamp.rs | 74 ++++++++++++++++++++++++++----- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index 7d8c92b..fca38e1 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -233,6 +233,11 @@ pub fn setup( let dma_streams = hal::dma::dma::StreamsTuple::new(device.DMA1, ccdr.peripheral.DMA1); + // Early, before the DMA1 peripherals (#272) + #[cfg(feature = "pounder_v1_1")] + let dma2_streams = + hal::dma::dma::StreamsTuple::new(device.DMA2, ccdr.peripheral.DMA2); + // Configure timer 2 to trigger conversions for the ADC let mut sampling_timer = { // The timer frequency is manually adjusted below, so the 1KHz setting here is a @@ -941,6 +946,7 @@ pub fn setup( pounder::timestamp::Timestamper::new( timestamp_timer, + dma2_streams.0, tim8_channels.ch1, &mut sampling_timer, etr_pin, diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 1e8f862..0c06192 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -24,12 +24,27 @@ ///! schedule. use stm32h7xx_hal as hal; -use crate::hardware::timers; +use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer}; + +use crate::hardware::{design_parameters::SAMPLE_BUFFER_SIZE, timers}; + +// Three buffers are required for double buffered mode - 2 are owned by the DMA stream and 1 is the +// working data provided to the application. These buffers must exist in a DMA-accessible memory +// region. Note that AXISRAM is not initialized on boot, so their initial contents are undefined. +#[link_section = ".axisram.buffers"] +static mut BUF: [[u16; SAMPLE_BUFFER_SIZE]; 3] = [[0; SAMPLE_BUFFER_SIZE]; 3]; /// Software unit to timestamp stabilizer ADC samples using an external pounder reference clock. pub struct Timestamper { + next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>, timer: timers::PounderTimestampTimer, - capture_channel: timers::tim8::Channel1InputCapture, + transfer: Transfer< + hal::dma::dma::Stream0, + timers::tim8::Channel1InputCapture, + PeripheralToMemory, + &'static mut [u16; SAMPLE_BUFFER_SIZE], + hal::dma::DBTransfer, + >, } impl Timestamper { @@ -50,12 +65,18 @@ impl Timestamper { /// The new pounder timestamper in an operational state. pub fn new( mut timestamp_timer: timers::PounderTimestampTimer, + stream: hal::dma::dma::Stream0, capture_channel: timers::tim8::Channel1, sampling_timer: &mut timers::SamplingTimer, _clock_input: hal::gpio::gpioa::PA0< hal::gpio::Alternate, >, ) -> Self { + let config = DmaConfig::default() + .memory_increment(true) + .circular_buffer(true) + .double_buffer(true); + // The sampling timer should generate a trigger output when CH1 comparison occurs. sampling_timer.generate_trigger(timers::TriggerGenerator::ComparePulse); @@ -66,29 +87,62 @@ impl Timestamper { // The capture channel should capture whenever the trigger input occurs. let input_capture = capture_channel .into_input_capture(timers::tim8::CaptureSource1::TRC); + input_capture.listen_dma(); + + // The data transfer is always a transfer of data from the peripheral to a RAM buffer. + let data_transfer: Transfer<_, _, PeripheralToMemory, _, _> = + Transfer::init( + stream, + input_capture, + // Note(unsafe): BUF[0] and BUF[1] are "owned" by this peripheral. + // They shall not be used anywhere else in the module. + unsafe { &mut BUF[0] }, + unsafe { Some(&mut BUF[1]) }, + config, + ); Self { timer: timestamp_timer, - capture_channel: input_capture, + transfer: data_transfer, + + // Note(unsafe): BUF[2] is "owned" by this peripheral. It shall not be used anywhere + // else in the module. + next_buffer: unsafe { Some(&mut BUF[2]) }, } } - /// Start collecting timestamps. + /// Start the DMA transfer for collecting timestamps. + #[allow(dead_code)] pub fn start(&mut self) { - self.capture_channel.enable(); + self.transfer + .start(|capture_channel| capture_channel.enable()); } /// Update the period of the underlying timestamp timer. + #[allow(dead_code)] pub fn update_period(&mut self, period: u16) { self.timer.set_period_ticks(period); } - /// Obtain a timestamp. + /// Obtain a buffer filled with timestamps. /// /// # Returns - /// A `Result` potentially indicating capture overflow and containing a `Option` of a captured - /// timestamp. - pub fn latest_timestamp(&mut self) -> Result, Option> { - self.capture_channel.latest_capture() + /// A reference to the underlying buffer that has been filled with timestamps. + #[allow(dead_code)] + pub fn acquire_buffer(&mut self) -> &[u16; SAMPLE_BUFFER_SIZE] { + // Wait for the transfer to fully complete before continuing. + // Note: If a device hangs up, check that this conditional is passing correctly, as there is + // no time-out checks here in the interest of execution speed. + while !self.transfer.get_transfer_complete_flag() {} + + let next_buffer = self.next_buffer.take().unwrap(); + + // Start the next transfer. + let (prev_buffer, _, _) = + self.transfer.next_transfer(next_buffer).unwrap(); + + self.next_buffer.replace(prev_buffer); // .unwrap_none() https://github.com/rust-lang/rust/issues/62633 + + self.next_buffer.as_ref().unwrap() } } From 18b6e99b10c730a861c1c52a6399fe12efa3655f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 17:32:06 +0200 Subject: [PATCH 25/40] fix a few clippy lints on files that are touched --- src/hardware/adc.rs | 1 + src/hardware/dac.rs | 3 ++- src/net/miniconf_client.rs | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hardware/adc.rs b/src/hardware/adc.rs index 8919479..26752f7 100644 --- a/src/hardware/adc.rs +++ b/src/hardware/adc.rs @@ -81,6 +81,7 @@ use hal::dma::{ #[derive(Copy, Clone)] pub struct AdcCode(pub u16); +#[allow(clippy::from_over_into)] impl Into for AdcCode { /// Convert raw ADC codes to/from voltage levels. /// diff --git a/src/hardware/dac.rs b/src/hardware/dac.rs index 013c43a..9b42e37 100644 --- a/src/hardware/dac.rs +++ b/src/hardware/dac.rs @@ -73,6 +73,7 @@ static mut DAC_BUF: [[SampleBuffer; 2]; 2] = [[[0; SAMPLE_BUFFER_SIZE]; 2]; 2]; #[derive(Copy, Clone)] pub struct DacCode(pub u16); +#[allow(clippy::from_over_into)] impl Into for DacCode { fn into(self) -> f32 { // The DAC output range in bipolar mode (including the external output op-amp) is +/- 4.096 @@ -104,7 +105,7 @@ macro_rules! dac_output { _channel: timers::tim2::$trigger_channel, spi: hal::spi::Spi, ) -> Self { - Self { _channel, spi } + Self { spi, _channel } } /// Start the SPI and begin operating in a DMA-driven transfer mode. diff --git a/src/net/miniconf_client.rs b/src/net/miniconf_client.rs index e5f328a..f280318 100644 --- a/src/net/miniconf_client.rs +++ b/src/net/miniconf_client.rs @@ -103,7 +103,7 @@ where let path = match topic.strip_prefix(prefix) { // For paths, we do not want to include the leading slash. Some(path) => { - if path.len() > 0 { + if !path.is_empty() { &path[1..] } else { path @@ -117,9 +117,9 @@ where let message: SettingsResponse = settings .string_set(path.split('/').peekable(), message) - .and_then(|_| { + .map(|_| { update = true; - Ok(()) + () }) .into(); From 3b737836350a5464a5cb6367dd30144e4cf92510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 17:45:14 +0200 Subject: [PATCH 26/40] clippy recursion --- src/net/miniconf_client.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/net/miniconf_client.rs b/src/net/miniconf_client.rs index f280318..4eaab07 100644 --- a/src/net/miniconf_client.rs +++ b/src/net/miniconf_client.rs @@ -119,7 +119,6 @@ where .string_set(path.split('/').peekable(), message) .map(|_| { update = true; - () }) .into(); From 93081c25c2978077a6fcb677b107b7adf8974f2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 17:55:42 +0200 Subject: [PATCH 27/40] refactor flatten_closures --- src/bin/dual-iir.rs | 11 +---------- src/bin/lockin.rs | 13 ++----------- src/lib.rs | 13 +++++++++++++ 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index ab965ee..da5af3d 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{fence, Ordering}; -use stabilizer::{hardware, net}; +use stabilizer::{flatten_closures, hardware, net}; use miniconf::Miniconf; use serde::Deserialize; @@ -52,15 +52,6 @@ impl Default for Settings { } } -macro_rules! flatten_closures { - ($fn:ident, $e:ident, $fun:block) => { - $e.$fn(|$e| $fun ).unwrap() - }; - ($fn:ident, $e:ident, $($es:ident),+, $fun:block) => { - $e.$fn(|$e| flatten_closures!($fn, $($es),*, $fun)).unwrap() - }; -} - #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] const APP: () = { struct Resources { diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index dd20278..019ef01 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -10,9 +10,9 @@ use serde::Deserialize; use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL}; -use stabilizer::net; +use stabilizer::{flatten_closures, hardware, net}; -use stabilizer::hardware::{ +use hardware::{ design_parameters, setup, Adc0Input, Adc1Input, AdcCode, AfeGain, Dac0Output, Dac1Output, DacCode, DigitalInput0, DigitalInput1, InputStamper, SystemTimer, AFE0, AFE1, @@ -80,15 +80,6 @@ impl Default for Settings { } } -macro_rules! flatten_closures { - ($fn:ident, $e:ident, $fun:block) => { - $e.$fn(|$e| $fun ).unwrap() - }; - ($fn:ident, $e:ident, $($es:ident),+, $fun:block) => { - $e.$fn(|$e| flatten_closures!($fn, $($es),*, $fun)).unwrap() - }; -} - #[rtic::app(device = stm32h7xx_hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] const APP: () = { struct Resources { diff --git a/src/lib.rs b/src/lib.rs index 85964a7..e1fdecf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,3 +3,16 @@ pub mod hardware; pub mod net; + +/// Macro to reduce rightward drift when calling the same closure-based API +/// on multiple structs simultaneously, e.g. when accessing DMA buffers. +/// This could be improved a bit using the tuple-based style from `mutex-trait`. +#[macro_export] +macro_rules! flatten_closures { + ($fn:ident, $e:ident, $fun:block) => { + $e.$fn(|$e| $fun ).unwrap() + }; + ($fn:ident, $e:ident, $($es:ident),+, $fun:block) => { + $e.$fn(|$e| flatten_closures!($fn, $($es),*, $fun)).unwrap() + }; +} From 28aef35d8779871524a4f25a6329688967900dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Mon, 31 May 2021 11:42:09 +0200 Subject: [PATCH 28/40] miniconf.py: make retain an option --- miniconf.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/miniconf.py b/miniconf.py index 7f88f65..542f29b 100644 --- a/miniconf.py +++ b/miniconf.py @@ -16,6 +16,7 @@ from gmqtt import Client as MqttClient LOGGER = logging.getLogger(__name__) + class Miniconf: """An asynchronous API for controlling Miniconf devices using MQTT.""" @@ -51,22 +52,21 @@ class Miniconf: _qos: The quality-of-service level of the received packet properties: A dictionary of properties associated with the message. """ - # Extract corrleation data from the properties - correlation_data = json.loads(properties['correlation_data'][0].decode('ascii')) - - # Get the request ID from the correlation data - request_id = correlation_data['request_id'] + # Extract request_id corrleation data from the properties + request_id = int.from_bytes( + properties['correlation_data'][0], 'big') self.inflight[request_id].set_result(json.loads(payload)) del self.inflight[request_id] - - async def command(self, path, value): + async def command(self, path, value, retain=True): """Write the provided data to the specified path. Args: path: The path to write the message to. value: The value to write to the path. + retain: Retain the MQTT message changing the setting + by the broker. Returns: The response to the command as a dictionary. @@ -79,16 +79,14 @@ class Miniconf: self.request_id += 1 assert request_id not in self.inflight, 'Invalid ID encountered' - correlation_data = json.dumps({ - 'request_id': request_id, - }).encode('ascii') + correlation_data = request_id.to_bytes(4, 'big') value = json.dumps(value) LOGGER.info('Sending %s to "%s"', value, setting_topic) fut = asyncio.get_running_loop().create_future() self.inflight[request_id] = fut - self.client.publish(setting_topic, payload=value, qos=0, retain=True, + self.client.publish(setting_topic, payload=value, qos=0, retain=retain, response_topic=response_topic, correlation_data=correlation_data) return await fut @@ -107,6 +105,9 @@ def main(): help='Increase logging verbosity') parser.add_argument('--broker', '-b', default='mqtt', type=str, help='The MQTT broker address') + parser.add_argument('--no-retain', '-n', default=False, + action='store_true', + help='Do not retain the affected settings') parser.add_argument('prefix', type=str, help='The MQTT topic prefix of the target') parser.add_argument('settings', metavar="KEY=VALUE", nargs='+', @@ -124,7 +125,8 @@ def main(): interface = await Miniconf.create(args.prefix, args.broker) for key_value in args.settings: path, value = key_value.split("=", 1) - response = await interface.command(path, json.loads(value)) + response = await interface.command(path, json.loads(value), + not args.no_retain) print(f'{path}: {response}') if response['code'] != 0: return response['code'] From af874c2eef712089436d594b45ff85d0d7a6a360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Tue, 1 Jun 2021 18:32:01 +0200 Subject: [PATCH 29/40] miniconf: add some checks, simplify --- miniconf.py | 59 ++++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/miniconf.py b/miniconf.py index 542f29b..ca47248 100644 --- a/miniconf.py +++ b/miniconf.py @@ -34,30 +34,33 @@ class Miniconf: client: A connected MQTT5 client. prefix: The MQTT toptic prefix of the device to control. """ - self.uuid = uuid.uuid1() self.request_id = 0 self.client = client self.prefix = prefix self.inflight = {} self.client.on_message = self._handle_response - self.client.subscribe(f'{prefix}/response/{self.uuid.hex}') + self.response_topic = f'{prefix}/response/{uuid.uuid1().hex}' + self.client.subscribe(self.response_topic) - def _handle_response(self, _client, _topic, payload, _qos, properties): + def _handle_response(self, _client, topic, payload, _qos, properties): """Callback function for when messages are received over MQTT. Args: _client: The MQTT client. - _topic: The topic that the message was received on. + topic: The topic that the message was received on. payload: The payload of the message. _qos: The quality-of-service level of the received packet properties: A dictionary of properties associated with the message. """ - # Extract request_id corrleation data from the properties - request_id = int.from_bytes( - properties['correlation_data'][0], 'big') + if topic == self.response_topic: + # Extract request_id corrleation data from the properties + request_id = int.from_bytes( + properties['correlation_data'][0], 'big') - self.inflight[request_id].set_result(json.loads(payload)) - del self.inflight[request_id] + self.inflight[request_id].set_result(json.loads(payload)) + del self.inflight[request_id] + else: + LOGGER.warn('Unexpected message on "%s"', topic) async def command(self, path, value, retain=True): """Write the provided data to the specified path. @@ -71,24 +74,24 @@ class Miniconf: Returns: The response to the command as a dictionary. """ - setting_topic = f'{self.prefix}/settings/{path}' - response_topic = f'{self.prefix}/response/{self.uuid.hex}' + topic = f'{self.prefix}/settings/{path}' - # Assign a unique identifier to this update request. - request_id = self.request_id - self.request_id += 1 - assert request_id not in self.inflight, 'Invalid ID encountered' - - correlation_data = request_id.to_bytes(4, 'big') - - value = json.dumps(value) - LOGGER.info('Sending %s to "%s"', value, setting_topic) fut = asyncio.get_running_loop().create_future() - self.inflight[request_id] = fut - self.client.publish(setting_topic, payload=value, qos=0, retain=retain, - response_topic=response_topic, - correlation_data=correlation_data) + # Assign unique correlation data for response dispatch + assert self.request_id not in self.inflight + self.inflight[self.request_id] = fut + correlation_data = self.request_id.to_bytes(4, 'big') + self.request_id += 1 + + payload = json.dumps(value) + LOGGER.info('Sending "%s" to "%s"', value, topic) + + self.client.publish( + topic, payload=payload, qos=0, retain=retain, + response_topic=self.response_topic, + correlation_data=correlation_data) + return await fut @@ -98,7 +101,7 @@ def main(): description='Miniconf command line interface.', formatter_class=argparse.RawDescriptionHelpFormatter, epilog='''Examples: -%(prog)s dt/sinara/stabilizer afe/0='"G2"' iir_ch/0/0=\ +%(prog)s dt/sinara/stabilizer/00-11-22-33-aa-bb afe/0='"G2"' iir_ch/0/0=\ '{"y_min": -32767, "y_max": 32767, "y_offset": 0, "ba": [1.0, 0, 0, 0, 0]}' ''') parser.add_argument('-v', '--verbose', action='count', default=0, @@ -110,7 +113,7 @@ def main(): help='Do not retain the affected settings') parser.add_argument('prefix', type=str, help='The MQTT topic prefix of the target') - parser.add_argument('settings', metavar="KEY=VALUE", nargs='+', + parser.add_argument('settings', metavar="PATH=VALUE", nargs='+', help='JSON encoded values for settings path keys.') args = parser.parse_args() @@ -123,8 +126,8 @@ def main(): async def configure_settings(): interface = await Miniconf.create(args.prefix, args.broker) - for key_value in args.settings: - path, value = key_value.split("=", 1) + for setting in args.settings: + path, value = setting.split("=", 1) response = await interface.command(path, json.loads(value), not args.no_retain) print(f'{path}: {response}') From 35536c062347a0741aecaedefe241a844a8b6018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 3 Jun 2021 10:31:11 +0200 Subject: [PATCH 30/40] Revert "Revert "pounder timestmper: don't use DMA"" This reverts commit d97ee3f0c427b4f7b9c5af458d15fb9be7dc6653. --- src/hardware/configuration.rs | 6 --- src/hardware/pounder/timestamp.rs | 74 +++++-------------------------- 2 files changed, 10 insertions(+), 70 deletions(-) diff --git a/src/hardware/configuration.rs b/src/hardware/configuration.rs index fca38e1..7d8c92b 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/configuration.rs @@ -233,11 +233,6 @@ pub fn setup( let dma_streams = hal::dma::dma::StreamsTuple::new(device.DMA1, ccdr.peripheral.DMA1); - // Early, before the DMA1 peripherals (#272) - #[cfg(feature = "pounder_v1_1")] - let dma2_streams = - hal::dma::dma::StreamsTuple::new(device.DMA2, ccdr.peripheral.DMA2); - // Configure timer 2 to trigger conversions for the ADC let mut sampling_timer = { // The timer frequency is manually adjusted below, so the 1KHz setting here is a @@ -946,7 +941,6 @@ pub fn setup( pounder::timestamp::Timestamper::new( timestamp_timer, - dma2_streams.0, tim8_channels.ch1, &mut sampling_timer, etr_pin, diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 0c06192..1e8f862 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -24,27 +24,12 @@ ///! schedule. use stm32h7xx_hal as hal; -use hal::dma::{dma::DmaConfig, PeripheralToMemory, Transfer}; - -use crate::hardware::{design_parameters::SAMPLE_BUFFER_SIZE, timers}; - -// Three buffers are required for double buffered mode - 2 are owned by the DMA stream and 1 is the -// working data provided to the application. These buffers must exist in a DMA-accessible memory -// region. Note that AXISRAM is not initialized on boot, so their initial contents are undefined. -#[link_section = ".axisram.buffers"] -static mut BUF: [[u16; SAMPLE_BUFFER_SIZE]; 3] = [[0; SAMPLE_BUFFER_SIZE]; 3]; +use crate::hardware::timers; /// Software unit to timestamp stabilizer ADC samples using an external pounder reference clock. pub struct Timestamper { - next_buffer: Option<&'static mut [u16; SAMPLE_BUFFER_SIZE]>, timer: timers::PounderTimestampTimer, - transfer: Transfer< - hal::dma::dma::Stream0, - timers::tim8::Channel1InputCapture, - PeripheralToMemory, - &'static mut [u16; SAMPLE_BUFFER_SIZE], - hal::dma::DBTransfer, - >, + capture_channel: timers::tim8::Channel1InputCapture, } impl Timestamper { @@ -65,18 +50,12 @@ impl Timestamper { /// The new pounder timestamper in an operational state. pub fn new( mut timestamp_timer: timers::PounderTimestampTimer, - stream: hal::dma::dma::Stream0, capture_channel: timers::tim8::Channel1, sampling_timer: &mut timers::SamplingTimer, _clock_input: hal::gpio::gpioa::PA0< hal::gpio::Alternate, >, ) -> Self { - let config = DmaConfig::default() - .memory_increment(true) - .circular_buffer(true) - .double_buffer(true); - // The sampling timer should generate a trigger output when CH1 comparison occurs. sampling_timer.generate_trigger(timers::TriggerGenerator::ComparePulse); @@ -87,62 +66,29 @@ impl Timestamper { // The capture channel should capture whenever the trigger input occurs. let input_capture = capture_channel .into_input_capture(timers::tim8::CaptureSource1::TRC); - input_capture.listen_dma(); - - // The data transfer is always a transfer of data from the peripheral to a RAM buffer. - let data_transfer: Transfer<_, _, PeripheralToMemory, _, _> = - Transfer::init( - stream, - input_capture, - // Note(unsafe): BUF[0] and BUF[1] are "owned" by this peripheral. - // They shall not be used anywhere else in the module. - unsafe { &mut BUF[0] }, - unsafe { Some(&mut BUF[1]) }, - config, - ); Self { timer: timestamp_timer, - transfer: data_transfer, - - // Note(unsafe): BUF[2] is "owned" by this peripheral. It shall not be used anywhere - // else in the module. - next_buffer: unsafe { Some(&mut BUF[2]) }, + capture_channel: input_capture, } } - /// Start the DMA transfer for collecting timestamps. - #[allow(dead_code)] + /// Start collecting timestamps. pub fn start(&mut self) { - self.transfer - .start(|capture_channel| capture_channel.enable()); + self.capture_channel.enable(); } /// Update the period of the underlying timestamp timer. - #[allow(dead_code)] pub fn update_period(&mut self, period: u16) { self.timer.set_period_ticks(period); } - /// Obtain a buffer filled with timestamps. + /// Obtain a timestamp. /// /// # Returns - /// A reference to the underlying buffer that has been filled with timestamps. - #[allow(dead_code)] - pub fn acquire_buffer(&mut self) -> &[u16; SAMPLE_BUFFER_SIZE] { - // Wait for the transfer to fully complete before continuing. - // Note: If a device hangs up, check that this conditional is passing correctly, as there is - // no time-out checks here in the interest of execution speed. - while !self.transfer.get_transfer_complete_flag() {} - - let next_buffer = self.next_buffer.take().unwrap(); - - // Start the next transfer. - let (prev_buffer, _, _) = - self.transfer.next_transfer(next_buffer).unwrap(); - - self.next_buffer.replace(prev_buffer); // .unwrap_none() https://github.com/rust-lang/rust/issues/62633 - - self.next_buffer.as_ref().unwrap() + /// A `Result` potentially indicating capture overflow and containing a `Option` of a captured + /// timestamp. + pub fn latest_timestamp(&mut self) -> Result, Option> { + self.capture_channel.latest_capture() } } From 2ba9e9c2f71f6ee781eb379865c71140d5f886cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 3 Jun 2021 07:57:18 +0000 Subject: [PATCH 31/40] pounder_timestamper: use input capture prescaler --- src/hardware/pounder/timestamp.rs | 14 +++++++++++--- src/hardware/timers.rs | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index 1e8f862..a527f7f 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -22,10 +22,10 @@ ///! mode. As soon as the DMA transfer completes, the hardware automatically swaps over to a second ///! buffer to continue capturing. This alleviates timing sensitivities of the DMA transfer ///! schedule. +use crate::hardware::{design_parameters, timers}; +use core::convert::TryFrom; use stm32h7xx_hal as hal; -use crate::hardware::timers; - /// Software unit to timestamp stabilizer ADC samples using an external pounder reference clock. pub struct Timestamper { timer: timers::PounderTimestampTimer, @@ -64,9 +64,17 @@ impl Timestamper { timestamp_timer.set_trigger_source(timers::TriggerSource::Trigger1); // The capture channel should capture whenever the trigger input occurs. - let input_capture = capture_channel + let mut input_capture = capture_channel .into_input_capture(timers::tim8::CaptureSource1::TRC); + // Capture at the batch period. + input_capture.configure_prescaler( + timers::Prescaler::try_from( + design_parameters::SAMPLE_BUFFER_SIZE_LOG2, + ) + .unwrap(), + ); + Self { timer: timestamp_timer, capture_channel: input_capture, diff --git a/src/hardware/timers.rs b/src/hardware/timers.rs index 78d27b6..23e15ca 100644 --- a/src/hardware/timers.rs +++ b/src/hardware/timers.rs @@ -1,5 +1,6 @@ ///! The sampling timer is used for managing ADC sampling and external reference timestamping. use super::hal; +use num_enum::TryFromPrimitive; use hal::stm32::{ // TIM1 and TIM8 have identical registers. @@ -34,6 +35,8 @@ pub enum TriggerSource { /// Prescalers for externally-supplied reference clocks. #[allow(dead_code)] +#[derive(TryFromPrimitive)] +#[repr(u8)] pub enum Prescaler { Div1 = 0b00, Div2 = 0b01, @@ -353,6 +356,21 @@ macro_rules! timer_channels { let regs = unsafe { &*<$TY>::ptr() }; regs.[< $ccmrx _input >]().modify(|_, w| w.[< ic $index f >]().bits(filter as u8)); } + + /// Configure the input capture prescaler. + /// + /// # Args + /// * `psc` - Prescaler exponent. + #[allow(dead_code)] + pub fn configure_prescaler(&mut self, prescaler: super::Prescaler) { + // Note(unsafe): This channel owns all access to the specific timer channel. + // Only atomic operations on completed on the timer registers. + let regs = unsafe { &*<$TY>::ptr() }; + // Note(unsafe): Enum values are all valid. + #[allow(unused_unsafe)] + regs.[< $ccmrx _input >]().modify(|_, w| unsafe { + w.[< ic $index psc >]().bits(prescaler as u8)}); + } } // Note(unsafe): This manually implements DMA support for input-capture channels. This From 971bc1109d7e78aa65c5744dfe0c36c437a35bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 3 Jun 2021 15:33:09 +0000 Subject: [PATCH 32/40] pll: add advance() --- dsp/src/pll.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dsp/src/pll.rs b/dsp/src/pll.rs index 8df750f..989facd 100644 --- a/dsp/src/pll.rs +++ b/dsp/src/pll.rs @@ -78,6 +78,13 @@ impl PLL { self.y = self.y.wrapping_add(f); (self.y, f) } + + /// Advance the PLL without providing a new timestamp. + pub fn advance(&mut self) -> (i32, i32) { + self.x = self.x.wrapping_add(self.f); + self.y = self.y.wrapping_add(self.f); + (self.y, self.f) + } } #[cfg(test)] From 10da2d38ae8df8cdfbf8db94a89797c9a3acae9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 3 Jun 2021 16:03:54 +0000 Subject: [PATCH 33/40] pll: merge advance into update (like rpll) --- dsp/src/pll.rs | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/dsp/src/pll.rs b/dsp/src/pll.rs index 989facd..09a5464 100644 --- a/dsp/src/pll.rs +++ b/dsp/src/pll.rs @@ -45,7 +45,7 @@ impl PLL { /// The signal's phase/frequency is reconstructed relative to the sampling period. /// /// Args: - /// * `x`: New input phase sample. + /// * `x`: New input phase sample or None if a sample has been missed. /// * `shift_frequency`: Frequency error scaling. The frequency gain per update is /// `1/(1 << shift_frequency)`. /// * `shift_phase`: Phase error scaling. The phase gain is `1/(1 << shift_phase)` @@ -55,36 +55,34 @@ impl PLL { /// A tuple of instantaneous phase and frequency (the current phase increment). pub fn update( &mut self, - x: i32, + x: Option, shift_frequency: u8, shift_phase: u8, ) -> (i32, i32) { debug_assert!((1..=30).contains(&shift_frequency)); debug_assert!((1..=30).contains(&shift_phase)); - let e = x.wrapping_sub(self.f); - self.f = self.f.wrapping_add( - (1i32 << (shift_frequency - 1)) - .wrapping_add(e) - .wrapping_sub(self.x) - >> shift_frequency, - ); - self.x = x; - let f = self.f.wrapping_add( - (1i32 << (shift_phase - 1)) - .wrapping_add(e) - .wrapping_sub(self.y) - >> shift_phase, - ); + let f = if let Some(x) = x { + let e = x.wrapping_sub(self.f); + self.f = self.f.wrapping_add( + (1i32 << (shift_frequency - 1)) + .wrapping_add(e) + .wrapping_sub(self.x) + >> shift_frequency, + ); + self.x = x; + self.f.wrapping_add( + (1i32 << (shift_phase - 1)) + .wrapping_add(e) + .wrapping_sub(self.y) + >> shift_phase, + ) + } else { + self.x = self.x.wrapping_add(self.f); + self.f + }; self.y = self.y.wrapping_add(f); (self.y, f) } - - /// Advance the PLL without providing a new timestamp. - pub fn advance(&mut self) -> (i32, i32) { - self.x = self.x.wrapping_add(self.f); - self.y = self.y.wrapping_add(self.f); - (self.y, self.f) - } } #[cfg(test)] From 3c574b3519086f3471055dab97fd94459de5b8eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Thu, 3 Jun 2021 19:24:25 +0200 Subject: [PATCH 34/40] pll: update tests and benches --- dsp/benches/micro.rs | 4 ++-- dsp/src/pll.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dsp/benches/micro.rs b/dsp/benches/micro.rs index c3588df..a449339 100644 --- a/dsp/benches/micro.rs +++ b/dsp/benches/micro.rs @@ -44,11 +44,11 @@ fn pll_bench() { let mut dut = PLL::default(); println!( "PLL::update(t, 12, 12): {}", - bench_env(0x241, |x| dut.update(*x, 12, 12)) + bench_env(Some(0x241), |x| dut.update(*x, 12, 12)) ); println!( "PLL::update(t, sf, sp): {}", - bench_env((0x241, 21, 20), |(x, p, q)| dut.update(*x, *p, *q)) + bench_env((Some(0x241), 21, 20), |(x, p, q)| dut.update(*x, *p, *q)) ); } diff --git a/dsp/src/pll.rs b/dsp/src/pll.rs index 09a5464..1c6a782 100644 --- a/dsp/src/pll.rs +++ b/dsp/src/pll.rs @@ -91,7 +91,7 @@ mod tests { #[test] fn mini() { let mut p = PLL::default(); - let (y, f) = p.update(0x10000, 8, 4); + let (y, f) = p.update(Some(0x10000), 8, 4); assert_eq!(y, 0x1100); assert_eq!(f, y); } @@ -105,7 +105,7 @@ mod tests { let mut x = 0i32; for i in 0..n { x = x.wrapping_add(f0); - let (y, f) = p.update(x, shift.0, shift.1); + let (y, f) = p.update(Some(x), shift.0, shift.1); if i > n / 4 { assert_eq!(f.wrapping_sub(f0).abs() <= 1, true); } From 73491fcb7577d03c873817f4faa9de1b395aa6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Jun 2021 10:45:22 +0200 Subject: [PATCH 35/40] pounder/timestamp: docs updatew --- src/hardware/pounder/timestamp.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/src/hardware/pounder/timestamp.rs b/src/hardware/pounder/timestamp.rs index a527f7f..7f8241d 100644 --- a/src/hardware/pounder/timestamp.rs +++ b/src/hardware/pounder/timestamp.rs @@ -13,15 +13,9 @@ ///! Once the timer is configured, an input capture is configured to record the timer count ///! register. The input capture is configured to utilize an internal trigger for the input capture. ///! The internal trigger is selected such that when a sample is generated on ADC0, the input -///! capture is simultaneously triggered. This results in the input capture triggering identically -///! to when the ADC samples the input. -///! -///! Once the input capture is properly configured, a DMA transfer is configured to collect all of -///! timestamps. The DMA transfer collects 1 timestamp for each ADC sample collected. In order to -///! avoid potentially losing a timestamp for a sample, the DMA transfer operates in double-buffer -///! mode. As soon as the DMA transfer completes, the hardware automatically swaps over to a second -///! buffer to continue capturing. This alleviates timing sensitivities of the DMA transfer -///! schedule. +///! capture is simultaneously triggered. That trigger is prescaled (its rate is divided) by the +///! batch size. This results in the input capture triggering identically to when the ADC samples +///! the last sample of the batch. That sample is then available for processing by the user. use crate::hardware::{design_parameters, timers}; use core::convert::TryFrom; use stm32h7xx_hal as hal; @@ -35,13 +29,8 @@ pub struct Timestamper { impl Timestamper { /// Construct the pounder sample timestamper. /// - /// # Note - /// The DMA is immediately configured after instantiation. It will not collect any samples - /// until the sample timer begins to cause input capture triggers. - /// /// # Args /// * `timestamp_timer` - The timer peripheral used for capturing timestamps from. - /// * `stream` - The DMA stream to use for collecting timestamps. /// * `capture_channel` - The input capture channel for collecting timestamps. /// * `sampling_timer` - The stabilizer ADC sampling timer. /// * `_clock_input` - The input pin for the external clock from Pounder. From d84c79af2ea369a6be9b2daa706c6b3a9645aa62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Jun 2021 10:50:09 +0200 Subject: [PATCH 36/40] apps: spi isrs are spi errors --- src/bin/dual-iir.rs | 8 ++++---- src/bin/lockin.rs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index da5af3d..c76ab1b 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -238,22 +238,22 @@ const APP: () = { #[task(binds = SPI2, priority = 3)] fn spi2(_: spi2::Context) { - panic!("ADC0 input overrun"); + panic!("ADC0 SPI error"); } #[task(binds = SPI3, priority = 3)] fn spi3(_: spi3::Context) { - panic!("ADC1 input overrun"); + panic!("ADC1 SPI error"); } #[task(binds = SPI4, priority = 3)] fn spi4(_: spi4::Context) { - panic!("DAC0 output error"); + panic!("DAC0 SPI error"); } #[task(binds = SPI5, priority = 3)] fn spi5(_: spi5::Context) { - panic!("DAC1 output error"); + panic!("DAC1 SPI error"); } extern "C" { diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index 019ef01..6dd7658 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -313,22 +313,22 @@ const APP: () = { #[task(binds = SPI2, priority = 3)] fn spi2(_: spi2::Context) { - panic!("ADC0 input overrun"); + panic!("ADC0 SPI error"); } #[task(binds = SPI3, priority = 3)] fn spi3(_: spi3::Context) { - panic!("ADC1 input overrun"); + panic!("ADC1 SPI error"); } #[task(binds = SPI4, priority = 3)] fn spi4(_: spi4::Context) { - panic!("DAC0 output error"); + panic!("DAC0 SPI error"); } #[task(binds = SPI5, priority = 3)] fn spi5(_: spi5::Context) { - panic!("DAC1 output error"); + panic!("DAC1 SPI error"); } extern "C" { From 77ae4363fd3e30f5327e7cad2a159f52726897dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Jun 2021 11:05:49 +0200 Subject: [PATCH 37/40] miniconf: update example usage --- miniconf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/miniconf.py b/miniconf.py index ca47248..644e026 100644 --- a/miniconf.py +++ b/miniconf.py @@ -101,8 +101,9 @@ def main(): description='Miniconf command line interface.', formatter_class=argparse.RawDescriptionHelpFormatter, epilog='''Examples: -%(prog)s dt/sinara/stabilizer/00-11-22-33-aa-bb afe/0='"G2"' iir_ch/0/0=\ -'{"y_min": -32767, "y_max": 32767, "y_offset": 0, "ba": [1.0, 0, 0, 0, 0]}' +%(prog)s dt/sinara/dual-iir/00-11-22-33-aa-bb iir_ch/0/0=\ +'{"y_min":-32767,"y_max":32767,"y_offset":0,"ba":[1.0,0,0,0,0]}' +%(prog)s dt/sinara/lockin/00-11-22-33-aa-bb afe/0='"G2"'\ ''') parser.add_argument('-v', '--verbose', action='count', default=0, help='Increase logging verbosity') From f60827e59a3bdf1b376c1634995687becd5bf3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Jun 2021 12:00:15 +0200 Subject: [PATCH 38/40] lockin: remove SPI error ISR Let them be handled by HardFault here. Keep them in dual-iir for debugging and show-casing their usage. --- src/bin/lockin.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index 270c94a..b2447e5 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -307,26 +307,6 @@ const APP: () = { unsafe { hal::ethernet::interrupt_handler() } } - #[task(binds = SPI2, priority = 3)] - fn spi2(_: spi2::Context) { - panic!("ADC0 SPI error"); - } - - #[task(binds = SPI3, priority = 3)] - fn spi3(_: spi3::Context) { - panic!("ADC1 SPI error"); - } - - #[task(binds = SPI4, priority = 3)] - fn spi4(_: spi4::Context) { - panic!("DAC0 SPI error"); - } - - #[task(binds = SPI5, priority = 3)] - fn spi5(_: spi5::Context) { - panic!("DAC1 SPI error"); - } - extern "C" { // hw interrupt handlers for RTIC to use for scheduling tasks // one per priority From f514205f8d5f56f46f627a5ca39abbd55cecc4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Jun 2021 17:02:01 +0200 Subject: [PATCH 39/40] stabilizer: don't flatten namespace, renames --- src/bin/dual-iir.rs | 29 ++++++++++------ src/bin/lockin.rs | 31 +++++++++++------ ...ital_input_stamper.rs => input_stamper.rs} | 0 src/hardware/mod.rs | 31 ++++++----------- src/hardware/pounder/mod.rs | 17 ++++------ src/hardware/{configuration.rs => setup.rs} | 18 +++++----- src/net/mod.rs | 33 ++++++++++--------- src/net/network_processor.rs | 2 +- src/net/telemetry.rs | 4 +-- 9 files changed, 85 insertions(+), 80 deletions(-) rename src/hardware/{digital_input_stamper.rs => input_stamper.rs} (100%) rename src/hardware/{configuration.rs => setup.rs} (98%) diff --git a/src/bin/dual-iir.rs b/src/bin/dual-iir.rs index e41ef05..d511ce1 100644 --- a/src/bin/dual-iir.rs +++ b/src/bin/dual-iir.rs @@ -3,18 +3,26 @@ #![no_main] use core::sync::atomic::{fence, Ordering}; -use miniconf::Miniconf; -use serde::Deserialize; use dsp::iir; use stabilizer::{ flatten_closures, hardware::{ - hal, setup, Adc0Input, Adc1Input, AdcCode, AfeGain, Dac0Output, - Dac1Output, DacCode, DigitalInput0, DigitalInput1, InputPin, - SystemTimer, AFE0, AFE1, + self, + adc::{Adc0Input, Adc1Input, AdcCode}, + afe::Gain, + dac::{Dac0Output, Dac1Output, DacCode}, + embedded_hal::digital::v2::InputPin, + hal, + system_timer::SystemTimer, + DigitalInput0, DigitalInput1, AFE0, AFE1, + }, + net::{ + miniconf::Miniconf, + serde::Deserialize, + telemetry::{Telemetry, TelemetryBuffer}, + NetworkState, NetworkUsers, }, - net::{NetworkState, NetworkUsers, Telemetry, TelemetryBuffer}, }; const SCALE: f32 = i16::MAX as _; @@ -24,7 +32,7 @@ const IIR_CASCADE_LENGTH: usize = 1; #[derive(Clone, Copy, Debug, Deserialize, Miniconf)] pub struct Settings { - afe: [AfeGain; 2], + afe: [Gain; 2], iir_ch: [[iir::IIR; IIR_CASCADE_LENGTH]; 2], allow_hold: bool, force_hold: bool, @@ -35,7 +43,7 @@ impl Default for Settings { fn default() -> Self { Self { // Analog frontend programmable gain amplifier gains (G1, G2, G5, G10) - afe: [AfeGain::G1, AfeGain::G1], + afe: [Gain::G1, Gain::G1], // IIR filter tap gains are an array `[b0, b1, b2, a1, a2]` such that the // new output is computed as `y0 = a1*y1 + a2*y2 + b0*x0 + b1*x1 + b2*x2`. // The array is `iir_state[channel-index][cascade-index][coeff-index]`. @@ -52,7 +60,7 @@ impl Default for Settings { } } -#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] +#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, monotonic = stabilizer::hardware::system_timer::SystemTimer)] const APP: () = { struct Resources { afes: (AFE0, AFE1), @@ -71,7 +79,8 @@ const APP: () = { #[init(spawn=[telemetry, settings_update])] fn init(c: init::Context) -> init::LateResources { // Configure the microcontroller - let (mut stabilizer, _pounder) = setup(c.core, c.device); + let (mut stabilizer, _pounder) = + hardware::setup::setup(c.core, c.device); let network = NetworkUsers::new( stabilizer.net.stack, diff --git a/src/bin/lockin.rs b/src/bin/lockin.rs index b2447e5..46ffdc0 100644 --- a/src/bin/lockin.rs +++ b/src/bin/lockin.rs @@ -3,18 +3,28 @@ #![no_main] use core::sync::atomic::{fence, Ordering}; -use miniconf::Miniconf; -use serde::Deserialize; use dsp::{Accu, Complex, ComplexExt, Lockin, RPLL}; use stabilizer::{ flatten_closures, hardware::{ - design_parameters, hal, setup, Adc0Input, Adc1Input, AdcCode, AfeGain, - Dac0Output, Dac1Output, DacCode, DigitalInput0, DigitalInput1, - InputPin, InputStamper, SystemTimer, AFE0, AFE1, + self, + adc::{Adc0Input, Adc1Input, AdcCode}, + afe::Gain, + dac::{Dac0Output, Dac1Output, DacCode}, + design_parameters, + embedded_hal::digital::v2::InputPin, + hal, + input_stamper::InputStamper, + system_timer::SystemTimer, + DigitalInput0, DigitalInput1, AFE0, AFE1, + }, + net::{ + miniconf::Miniconf, + serde::Deserialize, + telemetry::{Telemetry, TelemetryBuffer}, + NetworkState, NetworkUsers, }, - net::{NetworkState, NetworkUsers, Telemetry, TelemetryBuffer}, }; // A constant sinusoid to send on the DAC output. @@ -43,7 +53,7 @@ enum LockinMode { #[derive(Copy, Clone, Debug, Deserialize, Miniconf)] pub struct Settings { - afe: [AfeGain; 2], + afe: [Gain; 2], lockin_mode: LockinMode, pll_tc: [u8; 2], @@ -59,7 +69,7 @@ pub struct Settings { impl Default for Settings { fn default() -> Self { Self { - afe: [AfeGain::G1; 2], + afe: [Gain::G1; 2], lockin_mode: LockinMode::External, @@ -76,7 +86,7 @@ impl Default for Settings { } } -#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, monotonic = stabilizer::hardware::SystemTimer)] +#[rtic::app(device = stabilizer::hardware::hal::stm32, peripherals = true, monotonic = stabilizer::hardware::system_timer::SystemTimer)] const APP: () = { struct Resources { afes: (AFE0, AFE1), @@ -95,7 +105,8 @@ const APP: () = { #[init(spawn=[settings_update, telemetry])] fn init(c: init::Context) -> init::LateResources { // Configure the microcontroller - let (mut stabilizer, _pounder) = setup(c.core, c.device); + let (mut stabilizer, _pounder) = + hardware::setup::setup(c.core, c.device); let network = NetworkUsers::new( stabilizer.net.stack, diff --git a/src/hardware/digital_input_stamper.rs b/src/hardware/input_stamper.rs similarity index 100% rename from src/hardware/digital_input_stamper.rs rename to src/hardware/input_stamper.rs diff --git a/src/hardware/mod.rs b/src/hardware/mod.rs index 2e4cd46..422f7b9 100644 --- a/src/hardware/mod.rs +++ b/src/hardware/mod.rs @@ -1,28 +1,19 @@ +pub use embedded_hal; ///! Module for all hardware-specific setup of Stabilizer pub use stm32h7xx_hal as hal; -// Re-export for the DigitalInputs below: -pub use embedded_hal::digital::v2::InputPin; - -mod adc; -mod afe; -mod configuration; -mod cycle_counter; -mod dac; +pub mod adc; +pub mod afe; +pub mod cycle_counter; +pub mod dac; pub mod design_parameters; -mod digital_input_stamper; -mod eeprom; +pub mod input_stamper; pub mod pounder; -mod system_timer; -mod timers; +pub mod setup; +pub mod system_timer; -pub use adc::*; -pub use afe::{Gain as AfeGain, *}; -pub use cycle_counter::*; -pub use dac::*; -pub use digital_input_stamper::*; -pub use pounder::*; -pub use system_timer::*; +mod eeprom; +mod timers; // Type alias for the analog front-end (AFE) for ADC0. pub type AFE0 = afe::ProgrammableGainAmplifier< @@ -52,8 +43,6 @@ pub type NetworkStack = smoltcp_nal::NetworkStack< pub type EthernetPhy = hal::ethernet::phy::LAN8742A; -pub use configuration::{setup, PounderDevices, StabilizerDevices}; - #[inline(never)] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { diff --git a/src/hardware/pounder/mod.rs b/src/hardware/pounder/mod.rs index a69e5b0..a56446e 100644 --- a/src/hardware/pounder/mod.rs +++ b/src/hardware/pounder/mod.rs @@ -2,19 +2,14 @@ use super::hal; use embedded_hal::{adc::OneShot, blocking::spi::Transfer}; use serde::{Deserialize, Serialize}; -mod attenuators; -mod dds_output; -mod hrtimer; -mod rf_power; +pub mod attenuators; +pub mod dds_output; +pub mod hrtimer; +pub mod rf_power; #[cfg(feature = "pounder_v1_1")] pub mod timestamp; -pub use attenuators::*; -pub use dds_output::*; -pub use hrtimer::{Channel as HRTimerChannel, *}; -pub use rf_power::*; - pub enum GpioPin { Led4Green = 0, Led5Red = 1, @@ -327,7 +322,7 @@ impl PounderDevices { } } -impl AttenuatorInterface for PounderDevices { +impl attenuators::AttenuatorInterface for PounderDevices { /// Reset all of the attenuators to a power-on default state. fn reset_attenuators(&mut self) -> Result<(), Error> { self.mcp23017 @@ -374,7 +369,7 @@ impl AttenuatorInterface for PounderDevices { } } -impl PowerMeasurementInterface for PounderDevices { +impl rf_power::PowerMeasurementInterface for PounderDevices { /// Sample an ADC channel. /// /// Args: diff --git a/src/hardware/configuration.rs b/src/hardware/setup.rs similarity index 98% rename from src/hardware/configuration.rs rename to src/hardware/setup.rs index 57e7361..515dd51 100644 --- a/src/hardware/configuration.rs +++ b/src/hardware/setup.rs @@ -15,9 +15,10 @@ use smoltcp_nal::smoltcp; use embedded_hal::digital::v2::{InputPin, OutputPin}; use super::{ - adc, afe, cycle_counter::CycleCounter, dac, design_parameters, - digital_input_stamper, eeprom, pounder, system_timer, timers, DdsOutput, - DigitalInput0, DigitalInput1, EthernetPhy, NetworkStack, AFE0, AFE1, + adc, afe, cycle_counter::CycleCounter, dac, design_parameters, eeprom, + input_stamper::InputStamper, pounder, pounder::dds_output::DdsOutput, + system_timer, timers, DigitalInput0, DigitalInput1, EthernetPhy, + NetworkStack, AFE0, AFE1, }; pub struct NetStorage { @@ -84,7 +85,7 @@ pub struct StabilizerDevices { pub afes: (AFE0, AFE1), pub adcs: (adc::Adc0Input, adc::Adc1Input), pub dacs: (dac::Dac0Output, dac::Dac1Output), - pub timestamper: digital_input_stamper::InputStamper, + pub timestamper: InputStamper, pub adc_dac_timer: timers::SamplingTimer, pub timestamp_timer: timers::TimestampTimer, pub net: NetworkDevices, @@ -509,10 +510,7 @@ pub fn setup( let input_stamper = { let trigger = gpioa.pa3.into_alternate_af2(); - digital_input_stamper::InputStamper::new( - trigger, - timestamp_timer_channels.ch4, - ) + InputStamper::new(trigger, timestamp_timer_channels.ch4) }; let digital_inputs = { @@ -877,7 +875,7 @@ pub fn setup( .set_speed(hal::gpio::Speed::VeryHigh); // Configure the IO_Update signal for the DDS. - let mut hrtimer = pounder::HighResTimerE::new( + let mut hrtimer = pounder::hrtimer::HighResTimerE::new( device.HRTIM_TIME, device.HRTIM_MASTER, device.HRTIM_COMMON, @@ -889,7 +887,7 @@ pub fn setup( // is triggered after the QSPI write, which can take approximately 120nS, so // there is additional margin. hrtimer.configure_single_shot( - pounder::HRTimerChannel::Two, + pounder::hrtimer::Channel::Two, design_parameters::POUNDER_IO_UPDATE_DURATION, design_parameters::POUNDER_IO_UPDATE_DELAY, ); diff --git a/src/net/mod.rs b/src/net/mod.rs index fb12ea7..e9a2538 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -5,25 +5,28 @@ ///! telemetry (via MQTT), configuration of run-time settings (via MQTT + Miniconf), and live data ///! streaming over raw UDP/TCP sockets. This module encompasses the main processing routines ///! related to Stabilizer networking operations. +pub use heapless; +pub use miniconf; +pub use serde; + +pub mod messages; +pub mod miniconf_client; +pub mod network_processor; +pub mod shared; +pub mod telemetry; + +use crate::hardware::{cycle_counter::CycleCounter, EthernetPhy, NetworkStack}; +use messages::{MqttMessage, SettingsResponse}; +use miniconf_client::MiniconfClient; +use network_processor::NetworkProcessor; +use shared::NetworkManager; +use telemetry::TelemetryClient; + use core::fmt::Write; use heapless::String; -pub use miniconf::Miniconf; +use miniconf::Miniconf; use serde::Serialize; -mod messages; -mod miniconf_client; -mod network_processor; -mod shared; -mod telemetry; - -use crate::hardware::{CycleCounter, EthernetPhy, NetworkStack}; -use messages::{MqttMessage, SettingsResponse}; - -pub use miniconf_client::*; -pub use network_processor::*; -pub use shared::*; -pub use telemetry::*; - pub type NetworkReference = shared::NetworkStackProxy<'static, NetworkStack>; #[derive(Copy, Clone, PartialEq)] diff --git a/src/net/network_processor.rs b/src/net/network_processor.rs index a64d6e7..a8168d3 100644 --- a/src/net/network_processor.rs +++ b/src/net/network_processor.rs @@ -4,7 +4,7 @@ ///! The network processir is a small taks to regularly process incoming data over ethernet, handle ///! the ethernet PHY state, and reset the network as appropriate. use super::{NetworkReference, UpdateState}; -use crate::hardware::{CycleCounter, EthernetPhy}; +use crate::hardware::{cycle_counter::CycleCounter, EthernetPhy}; /// Processor for managing network hardware. pub struct NetworkProcessor { diff --git a/src/net/telemetry.rs b/src/net/telemetry.rs index d976fee..40a5ce3 100644 --- a/src/net/telemetry.rs +++ b/src/net/telemetry.rs @@ -16,7 +16,7 @@ use serde::Serialize; use super::NetworkReference; use crate::hardware::{ - design_parameters::MQTT_BROKER, AdcCode, AfeGain, DacCode, + adc::AdcCode, afe::Gain, dac::DacCode, design_parameters::MQTT_BROKER, }; /// The telemetry client for reporting telemetry data over MQTT. @@ -73,7 +73,7 @@ impl TelemetryBuffer { /// /// # Returns /// The finalized telemetry structure that can be serialized and reported. - pub fn finalize(self, afe0: AfeGain, afe1: AfeGain) -> Telemetry { + pub fn finalize(self, afe0: Gain, afe1: Gain) -> Telemetry { let in0_volts = Into::::into(self.adcs[0]) / afe0.as_multiplier(); let in1_volts = Into::::into(self.adcs[1]) / afe1.as_multiplier(); From a3c2d8a6d493c9ba60d5ff3ed313d526b2fbfc99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20J=C3=B6rdens?= Date: Fri, 4 Jun 2021 22:10:13 +0200 Subject: [PATCH 40/40] deps: bump indirect dependencies --- Cargo.lock | 82 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 11ae3fe..3a7f896 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -63,7 +63,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" dependencies = [ - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -104,11 +104,11 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cast" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" +checksum = "57cdfa5d50aad6cb4d44dcab6101a7f79925bd59d82ca42f38a9856a28865374" dependencies = [ - "rustc_version", + "rustc_version 0.3.3", ] [[package]] @@ -305,9 +305,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", @@ -374,9 +374,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" +checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" [[package]] name = "log" @@ -395,9 +395,9 @@ checksum = "c75de51135344a4f8ed3cfe2720dc27736f7711989703a0b43aadf3753c55577" [[package]] name = "matrixmultiply" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1300bdbea33ec2836b01ff1f5a6eed8bad66d0c31f94d9b7993407a8b054c3a1" +checksum = "5a8a15b776d9dfaecd44b03c5828c2199cddff5247215858aac14624f8d6b741" dependencies = [ "rawpointer", ] @@ -528,6 +528,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58" +[[package]] +name = "pest" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" +dependencies = [ + "ucd-trie", +] + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -536,9 +545,9 @@ checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "proc-macro2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid", ] @@ -640,7 +649,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", ] [[package]] @@ -655,7 +673,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", ] [[package]] @@ -664,6 +691,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "serde" version = "1.0.126" @@ -707,9 +743,9 @@ dependencies = [ [[package]] name = "smoltcp" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97173c1ef35b0a09304cb3882eba594761243005847cbbf6124f966e8da6519a" +checksum = "11b5647cc4676e9358e6b15b6536b34e5b413e5ae946a06b3f85e713132bcdfa" dependencies = [ "bitflags", "byteorder", @@ -793,9 +829,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.68" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ce15dd3ed8aa2f8eeac4716d6ef5ab58b6b9256db41d7e1a0224c2788e8fd87" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" dependencies = [ "proc-macro2", "quote", @@ -808,6 +844,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" +[[package]] +name = "ucd-trie" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" + [[package]] name = "ufmt-write" version = "0.1.0" @@ -816,9 +858,9 @@ checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "vcell"