Merge branch 'master' into feature/adc-dac-io-macros

* master:
  cargo: add docs for target cpu/features
  iir: more generic math helpers, use core::intrinsics
  cargo fmt [nfc]
  iir: vminnm/vmaxnm
  iir: fmt [nfc]
  iir: fix comment [nfc]
  cargo-config: cm7 features
  iir: copy_within is better than rotate_right
  processing: use faster unsafe truncate
This commit is contained in:
Robert Jördens 2020-12-02 14:56:52 +01:00
commit 31fcdcc97d
6 changed files with 67 additions and 17 deletions

View File

@ -1,6 +1,16 @@
[target.'cfg(all(target_arch = "arm", target_os = "none"))'] [target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "gdb-multiarch -q -x openocd.gdb" runner = "gdb-multiarch -q -x openocd.gdb"
rustflags = ["-C", "link-arg=-Tlink.x"] rustflags = [
"-C", "link-arg=-Tlink.x",
# The target (below) defaults to cortex-m4
# There currently are two different options to go beyond that:
# 1. cortex-m7 has the right flags and instructions (FPU) but no instruction schedule yet
"-C", "target-cpu=cortex-m7",
# 2. cortex-m4 with the additional fpv5 instructions and a potentially
# better-than-nothing instruction schedule
"-C", "target-feature=+fp-armv8d16",
# When combined they are equivalent to (1) alone
]
[build] [build]
target = "thumbv7em-none-eabihf" target = "thumbv7em-none-eabihf"

View File

@ -62,7 +62,7 @@ branch = "dma"
[features] [features]
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"] semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
bkpt = [ ] bkpt = [ ]
nightly = ["cortex-m/inline-asm"] nightly = ["cortex-m/inline-asm", "dsp/nightly"]
[profile.dev] [profile.dev]
codegen-units = 1 codegen-units = 1

View File

@ -6,3 +6,6 @@ edition = "2018"
[dependencies] [dependencies]
serde = { version = "1.0", features = ["derive"], default-features = false } serde = { version = "1.0", features = ["derive"], default-features = false }
[features]
nightly = []

View File

@ -1,4 +1,4 @@
use core::ops::{Add, Mul}; use core::ops::{Add, Mul, Neg};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use core::f32; use core::f32;
@ -8,23 +8,35 @@ use core::f32;
// `compiler-intrinsics`/llvm should have better (robust, universal, and // `compiler-intrinsics`/llvm should have better (robust, universal, and
// faster) implementations. // faster) implementations.
fn abs(x: f32) -> f32 { fn abs<T>(x: T) -> T
if x >= 0. { where
T: PartialOrd + Default + Neg<Output = T>,
{
if x >= T::default() {
x x
} else { } else {
-x -x
} }
} }
fn copysign(x: f32, y: f32) -> f32 { fn copysign<T>(x: T, y: T) -> T
if (x >= 0. && y >= 0.) || (x <= 0. && y <= 0.) { where
T: PartialOrd + Default + Neg<Output = T>,
{
if (x >= T::default() && y >= T::default())
|| (x <= T::default() && y <= T::default())
{
x x
} else { } else {
-x -x
} }
} }
fn max(x: f32, y: f32) -> f32 { #[cfg(not(feature = "nightly"))]
fn max<T>(x: T, y: T) -> T
where
T: PartialOrd,
{
if x > y { if x > y {
x x
} else { } else {
@ -32,7 +44,11 @@ fn max(x: f32, y: f32) -> f32 {
} }
} }
fn min(x: f32, y: f32) -> f32 { #[cfg(not(feature = "nightly"))]
fn min<T>(x: T, y: T) -> T
where
T: PartialOrd,
{
if x < y { if x < y {
x x
} else { } else {
@ -40,6 +56,16 @@ fn min(x: f32, y: f32) -> f32 {
} }
} }
#[cfg(feature = "nightly")]
fn max(x: f32, y: f32) -> f32 {
core::intrinsics::maxnumf32(x, y)
}
#[cfg(feature = "nightly")]
fn min(x: f32, y: f32) -> f32 {
core::intrinsics::minnumf32(x, y)
}
// Multiply-accumulate vectors `x` and `a`. // Multiply-accumulate vectors `x` and `a`.
// //
// A.k.a. dot product. // A.k.a. dot product.
@ -50,7 +76,7 @@ where
{ {
x.iter() x.iter()
.zip(a) .zip(a)
.map(|(&x, &a)| x * a) .map(|(x, a)| *x * *a)
.fold(y0, |y, xa| y + xa) .fold(y0, |y, xa| y + xa)
} }
@ -58,10 +84,10 @@ where
/// ///
/// To represent the IIR state (input and output memory) during the filter update /// To represent the IIR state (input and output memory) during the filter update
/// this contains the three inputs (x0, x1, x2) and the two outputs (y1, y2) /// this contains the three inputs (x0, x1, x2) and the two outputs (y1, y2)
/// concatenated. /// concatenated. Lower indices correspond to more recent samples.
/// To represent the IIR coefficients, this contains the feed-forward /// To represent the IIR coefficients, this contains the feed-forward
/// coefficients (b0, b1, b2) followd by the feed-back coefficients (a1, a2), /// coefficients (b0, b1, b2) followd by the negated feed-back coefficients
/// all normalized such that a0 = 1. /// (-a1, -a2), all five normalized such that a0 = 1.
pub type IIRState = [f32; 5]; pub type IIRState = [f32; 5];
/// IIR configuration. /// IIR configuration.
@ -159,10 +185,13 @@ impl IIR {
/// * `xy` - Current filter state. /// * `xy` - Current filter state.
/// * `x0` - New input. /// * `x0` - New input.
pub fn update(&self, xy: &mut IIRState, x0: f32) -> f32 { pub fn update(&self, xy: &mut IIRState, x0: f32) -> f32 {
let n = self.ba.len();
debug_assert!(xy.len() == n);
// `xy` contains x0 x1 y0 y1 y2 // `xy` contains x0 x1 y0 y1 y2
// Increment time x1 x2 y1 y2 y3 // Increment time x1 x2 y1 y2 y3
// Rotate y3 x1 x2 y1 y2 // Shift x1 x1 x2 y1 y2
xy.rotate_right(1); // This unrolls better than xy.rotate_right(1)
xy.copy_within(0..n - 1, 1);
// Store x0 x0 x1 x2 y1 y2 // Store x0 x0 x1 x2 y1 y2
xy[0] = x0; xy[0] = x0;
// Compute y0 by multiply-accumulate // Compute y0 by multiply-accumulate
@ -170,7 +199,7 @@ impl IIR {
// Limit y0 // Limit y0
let y0 = max(self.y_min, min(self.y_max, y0)); let y0 = max(self.y_min, min(self.y_max, y0));
// Store y0 x0 x1 y0 y1 y2 // Store y0 x0 x1 y0 y1 y2
xy[xy.len() / 2] = y0; xy[n / 2] = y0;
y0 y0
} }
} }

View File

@ -1,3 +1,4 @@
#![no_std] #![no_std]
#![cfg_attr(feature = "nightly", feature(asm, core_intrinsics))]
pub mod iir; pub mod iir;

View File

@ -13,6 +13,9 @@
fn panic(_info: &core::panic::PanicInfo) -> ! { fn panic(_info: &core::panic::PanicInfo) -> ! {
let gpiod = unsafe { &*hal::stm32::GPIOD::ptr() }; let gpiod = unsafe { &*hal::stm32::GPIOD::ptr() };
gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3 gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3
#[cfg(feature = "nightly")]
core::intrinsics::abort();
#[cfg(not(feature = "nightly"))]
unsafe { unsafe {
core::intrinsics::abort(); core::intrinsics::abort();
} }
@ -760,7 +763,11 @@ const APP: () = {
let x = f32::from(adc_samples[channel][sample] as i16); let x = f32::from(adc_samples[channel][sample] as i16);
let y = c.resources.iir_ch[channel] let y = c.resources.iir_ch[channel]
.update(&mut c.resources.iir_state[channel], x); .update(&mut c.resources.iir_state[channel], x);
dac_samples[channel][sample] = y as i16 as u16 ^ 0x8000; // 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::<i16>() };
// Convert to DAC code
dac_samples[channel][sample] = y as u16 ^ 0x8000;
} }
} }
let [dac0, dac1] = dac_samples; let [dac0, dac1] = dac_samples;