Skip to content

Commit 708e31d

Browse files
bors[bot]jordens
andauthored
Merge #178
178: Feature/iir tweaks r=ryan-summers a=jordens Some minor IIR tweaks and a couple more relevant compiler flags. **Untested on hardware.** Co-authored-by: Robert Jördens <rj@quartiq.de>
2 parents 8ef6c06 + 8769194 commit 708e31d

6 files changed

Lines changed: 72 additions & 18 deletions

File tree

.cargo/config

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
22
runner = "gdb-multiarch -q -x openocd.gdb"
3-
rustflags = ["-C", "link-arg=-Tlink.x"]
3+
rustflags = [
4+
"-C", "link-arg=-Tlink.x",
5+
# The target (below) defaults to cortex-m4
6+
# There currently are two different options to go beyond that:
7+
# 1. cortex-m7 has the right flags and instructions (FPU) but no instruction schedule yet
8+
"-C", "target-cpu=cortex-m7",
9+
# 2. cortex-m4 with the additional fpv5 instructions and a potentially
10+
# better-than-nothing instruction schedule
11+
"-C", "target-feature=+fp-armv8d16",
12+
# When combined they are equivalent to (1) alone
13+
]
414

515
[build]
616
target = "thumbv7em-none-eabihf"

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ branch = "dma"
6262
[features]
6363
semihosting = ["panic-semihosting", "cortex-m-log/semihosting"]
6464
bkpt = [ ]
65-
nightly = ["cortex-m/inline-asm"]
65+
nightly = ["cortex-m/inline-asm", "dsp/nightly"]
6666

6767
[profile.dev]
6868
codegen-units = 1

dsp/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ edition = "2018"
66

77
[dependencies]
88
serde = { version = "1.0", features = ["derive"], default-features = false }
9+
10+
[features]
11+
nightly = []

dsp/src/iir.rs

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use core::ops::{Add, Mul};
1+
use core::ops::{Add, Mul, Neg};
22
use serde::{Deserialize, Serialize};
33

44
use core::f32;
@@ -8,38 +8,64 @@ use core::f32;
88
// `compiler-intrinsics`/llvm should have better (robust, universal, and
99
// faster) implementations.
1010

11-
fn abs(x: f32) -> f32 {
12-
if x >= 0. {
11+
fn abs<T>(x: T) -> T
12+
where
13+
T: PartialOrd + Default + Neg<Output = T>,
14+
{
15+
if x >= T::default() {
1316
x
1417
} else {
1518
-x
1619
}
1720
}
1821

19-
fn copysign(x: f32, y: f32) -> f32 {
20-
if (x >= 0. && y >= 0.) || (x <= 0. && y <= 0.) {
22+
fn copysign<T>(x: T, y: T) -> T
23+
where
24+
T: PartialOrd + Default + Neg<Output = T>,
25+
{
26+
if (x >= T::default() && y >= T::default())
27+
|| (x <= T::default() && y <= T::default())
28+
{
2129
x
2230
} else {
2331
-x
2432
}
2533
}
2634

27-
fn max(x: f32, y: f32) -> f32 {
35+
#[cfg(not(feature = "nightly"))]
36+
fn max<T>(x: T, y: T) -> T
37+
where
38+
T: PartialOrd,
39+
{
2840
if x > y {
2941
x
3042
} else {
3143
y
3244
}
3345
}
3446

35-
fn min(x: f32, y: f32) -> f32 {
47+
#[cfg(not(feature = "nightly"))]
48+
fn min<T>(x: T, y: T) -> T
49+
where
50+
T: PartialOrd,
51+
{
3652
if x < y {
3753
x
3854
} else {
3955
y
4056
}
4157
}
4258

59+
#[cfg(feature = "nightly")]
60+
fn max(x: f32, y: f32) -> f32 {
61+
core::intrinsics::maxnumf32(x, y)
62+
}
63+
64+
#[cfg(feature = "nightly")]
65+
fn min(x: f32, y: f32) -> f32 {
66+
core::intrinsics::minnumf32(x, y)
67+
}
68+
4369
// Multiply-accumulate vectors `x` and `a`.
4470
//
4571
// A.k.a. dot product.
@@ -50,18 +76,18 @@ where
5076
{
5177
x.iter()
5278
.zip(a)
53-
.map(|(&x, &a)| x * a)
79+
.map(|(x, a)| *x * *a)
5480
.fold(y0, |y, xa| y + xa)
5581
}
5682

5783
/// IIR state and coefficients type.
5884
///
5985
/// To represent the IIR state (input and output memory) during the filter update
6086
/// this contains the three inputs (x0, x1, x2) and the two outputs (y1, y2)
61-
/// concatenated.
87+
/// concatenated. Lower indices correspond to more recent samples.
6288
/// To represent the IIR coefficients, this contains the feed-forward
63-
/// coefficients (b0, b1, b2) followd by the feed-back coefficients (a1, a2),
64-
/// all normalized such that a0 = 1.
89+
/// coefficients (b0, b1, b2) followd by the negated feed-back coefficients
90+
/// (-a1, -a2), all five normalized such that a0 = 1.
6591
pub type IIRState = [f32; 5];
6692

6793
/// IIR configuration.
@@ -159,18 +185,21 @@ impl IIR {
159185
/// * `xy` - Current filter state.
160186
/// * `x0` - New input.
161187
pub fn update(&self, xy: &mut IIRState, x0: f32) -> f32 {
188+
let n = self.ba.len();
189+
debug_assert!(xy.len() == n);
162190
// `xy` contains x0 x1 y0 y1 y2
163191
// Increment time x1 x2 y1 y2 y3
164-
// Rotate y3 x1 x2 y1 y2
165-
xy.rotate_right(1);
192+
// Shift x1 x1 x2 y1 y2
193+
// This unrolls better than xy.rotate_right(1)
194+
xy.copy_within(0..n - 1, 1);
166195
// Store x0 x0 x1 x2 y1 y2
167196
xy[0] = x0;
168197
// Compute y0 by multiply-accumulate
169198
let y0 = macc(self.y_offset, xy, &self.ba);
170199
// Limit y0
171200
let y0 = max(self.y_min, min(self.y_max, y0));
172201
// Store y0 x0 x1 y0 y1 y2
173-
xy[xy.len() / 2] = y0;
202+
xy[n / 2] = y0;
174203
y0
175204
}
176205
}

dsp/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#![no_std]
2+
#![cfg_attr(feature = "nightly", feature(asm, core_intrinsics))]
23

34
pub mod iir;

src/main.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
fn panic(_info: &core::panic::PanicInfo) -> ! {
1414
let gpiod = unsafe { &*hal::stm32::GPIOD::ptr() };
1515
gpiod.odr.modify(|_, w| w.odr6().high().odr12().high()); // FP_LED_1, FP_LED_3
16+
#[cfg(feature = "nightly")]
17+
core::intrinsics::abort();
18+
#[cfg(not(feature = "nightly"))]
1619
unsafe {
1720
core::intrinsics::abort();
1821
}
@@ -760,14 +763,22 @@ const APP: () = {
760763
let x0 = f32::from(*adc0 as i16);
761764
let y0 = c.resources.iir_ch[0]
762765
.update(&mut c.resources.iir_state[0], x0);
763-
y0 as i16 as u16 ^ 0x8000
766+
// Note(unsafe): The filter limits ensure that the value is in range.
767+
// The truncation introduces 1/2 LSB distortion.
768+
let y0 = unsafe { y0.to_int_unchecked::<i16>() };
769+
// Convert to DAC code
770+
y0 as u16 ^ 0x8000
764771
};
765772

766773
dac1[i] = {
767774
let x1 = f32::from(*adc1 as i16);
768775
let y1 = c.resources.iir_ch[1]
769776
.update(&mut c.resources.iir_state[1], x1);
770-
y1 as i16 as u16 ^ 0x8000
777+
// Note(unsafe): The filter limits ensure that the value is in range.
778+
// The truncation introduces 1/2 LSB distortion.
779+
let y1 = unsafe { y1.to_int_unchecked::<i16>() };
780+
// Convert to DAC code
781+
y1 as u16 ^ 0x8000
771782
};
772783
}
773784

0 commit comments

Comments
 (0)