Skip to content

Commit c1f08f6

Browse files
committed
Add FMAC implementation
feat: Add FMAC support for digital signal processing Implement support for the STM32G4's Filter Math Accelerator (FMAC) peripheral, which provides hardware acceleration for digital signal processing operations including: - FIR filter implementation - IIR filter implementation (direct form 1) - Vector operations such as dot products The implementation includes: - Core FMAC peripheral control - Type states to constrain method access - Buffer management with const generic configurable layouts - DMA support for efficient data transfer
1 parent 10fb278 commit c1f08f6

File tree

8 files changed

+748
-2
lines changed

8 files changed

+748
-2
lines changed

Cargo.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ stm32g4 = { version = "0.16.0", features = ["atomics"] }
1717
paste = "1.0"
1818
fugit = "0.3.7"
1919
stm32-usbd = { version = "0.7.0", optional = true }
20-
fixed = { version = "1.28.0", optional = true }
20+
fixed = { version = "1.28.0" }
2121
embedded-io = "0.6"
2222

2323
[dependencies.cortex-m]
@@ -106,7 +106,8 @@ defmt = [
106106
"embedded-io/defmt-03",
107107
"embedded-test/defmt",
108108
]
109-
cordic = ["dep:fixed"]
109+
cordic = []
110+
fmac = []
110111
adc3 = []
111112
adc4 = []
112113
adc5 = []
@@ -153,6 +154,10 @@ required-features = ["usb"]
153154
name = "cordic"
154155
required-features = ["cordic"]
155156

157+
[[example]]
158+
name = "fmac"
159+
required-features = ["fmac"]
160+
156161
[[test]]
157162
name = "nucleo-g474"
158163
harness = false

examples/fmac.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#![deny(warnings)]
2+
#![deny(unsafe_code)]
3+
#![no_main]
4+
#![no_std]
5+
6+
use cortex_m::asm::wfi;
7+
use hal::prelude::*;
8+
use hal::stm32;
9+
use stm32g4xx_hal as hal;
10+
11+
use cortex_m_rt::entry;
12+
13+
#[macro_use]
14+
mod utils;
15+
16+
use utils::logger::info;
17+
18+
use stm32g4xx_hal::fmac::{
19+
buffer::{BufferLayout, Watermark},
20+
function::IIR,
21+
Buffer, FmacExt,
22+
};
23+
24+
use fixed::types::I1F15;
25+
26+
#[entry]
27+
fn main() -> ! {
28+
utils::logger::init();
29+
30+
info!("start");
31+
let dp = stm32::Peripherals::take().expect("cannot take peripherals");
32+
let mut rcc = dp.RCC.constrain();
33+
34+
// The FMAC has an internal memory area of 256x16bit words that must be allocated to the
35+
// three different buffers named X1 (input buffer), X2 (coefficients), and Y (output buffer).
36+
//
37+
// The BufferLayout struct takes three generic consts and will calculate the base offsets at compile time,
38+
// which must be passed as a generic parameter to the constrain method of the FMAC instance.
39+
//
40+
// Create an FMAC instance with a memory layout of 16 inputs, 2 coefficients, and 8 output buffer words
41+
//
42+
// For IIR filters, RM0440 indicates X2 coeffecient buffer should be 2N + 2M where
43+
// N is the number of feedforward coefficients and M is the number of feedback coefficients.
44+
//
45+
// Following constrain(), the buffer layout has been applied to the peripheral and cannot be changed.
46+
let mut fmac = dp
47+
.FMAC
48+
.constrain::<BufferLayout<32, 4, 1>>(&mut rcc)
49+
.reset();
50+
51+
// Configure an IIR filter with 1 feedforward, and 1 feedback coefficient,
52+
// with both coefficients set to 1.0.
53+
//
54+
// This is equivalent to a multiply accumulate operation
55+
// Y[n] = ∑ X1[n] * X2[0] + Y[n-1] * X2[1]
56+
//
57+
// The inputs will be set to ~0.01 (to nearest fixed point representation), and the output will increment
58+
// by 0.01 into Y for each input sample, and the final read of Y should be about 0.319
59+
60+
// Fill the input buffer with 0.01
61+
fmac.preload_buffer(Buffer::X1, |_index| I1F15::from_num(0.01));
62+
63+
// Fill the coefficients with I1F15::MAX (max value representable by I1F15, ~1.0)
64+
fmac.preload_buffer(Buffer::X2, |_index| I1F15::MAX);
65+
fmac.preload_buffer(Buffer::Y, |_index| I1F15::ZERO);
66+
67+
// Watermarks can be set on the X1 and Y buffers
68+
// to control when the input empty and output full
69+
// flags are asserted to cause early interrupts or DMA
70+
// to avoid underflow or overflow conditions.
71+
fmac.set_watermark(Buffer::X1, Watermark::Threshold1);
72+
fmac.set_watermark(Buffer::Y, Watermark::Threshold1);
73+
74+
// Select the IIR function, sepecifying the number of
75+
// feedforward and feedback coefficients, and the gain
76+
fmac.select_function(IIR {
77+
feedforward_coeffs: 1,
78+
feedback_coeffs: 1,
79+
gain: 0,
80+
});
81+
82+
let fmac = fmac.start();
83+
84+
info!("Input buffer full: {}", fmac.is_input_full());
85+
86+
info!("Waiting for result");
87+
while !fmac.is_result_available() {}
88+
89+
info!("Reading results");
90+
91+
let mut count = 0;
92+
loop {
93+
while let Some(output) = fmac.read() {
94+
count += 1;
95+
info!("Output {}: {}", count, output.to_num::<f32>());
96+
}
97+
}
98+
}

0 commit comments

Comments
 (0)