Skip to content

Commit 1ea476a

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 6abbaef commit 1ea476a

File tree

8 files changed

+765
-5
lines changed

8 files changed

+765
-5
lines changed

Cargo.toml

Lines changed: 24 additions & 5 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
stm32-hrtim = { version = "0.1.0", optional = true }
2323

@@ -82,9 +82,23 @@ usb = ["dep:stm32-usbd"]
8282
stm32g431 = ["stm32g4/stm32g431", "cat2"]
8383
stm32g441 = ["stm32g4/stm32g441", "cat2"]
8484
stm32g473 = ["stm32g4/stm32g473", "cat3", "adc3", "adc4", "adc5"]
85-
stm32g474 = ["stm32g4/stm32g474", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g474"]
85+
stm32g474 = [
86+
"stm32g4/stm32g474",
87+
"cat3",
88+
"adc3",
89+
"adc4",
90+
"adc5",
91+
"stm32-hrtim/stm32g474",
92+
]
8693
stm32g483 = ["stm32g4/stm32g483", "cat3", "adc3", "adc4", "adc5"]
87-
stm32g484 = ["stm32g4/stm32g484", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g484"]
94+
stm32g484 = [
95+
"stm32g4/stm32g484",
96+
"cat3",
97+
"adc3",
98+
"adc4",
99+
"adc5",
100+
"stm32-hrtim/stm32g484",
101+
]
88102
stm32g491 = ["stm32g4/stm32g491", "cat4", "adc3"]
89103
stm32g4a1 = ["stm32g4/stm32g4a1", "cat4", "adc3"]
90104

@@ -106,9 +120,10 @@ defmt = [
106120
"embedded-hal/defmt-03",
107121
"embedded-io/defmt-03",
108122
"embedded-test/defmt",
109-
"stm32-hrtim?/defmt"
123+
"stm32-hrtim?/defmt",
110124
]
111-
cordic = ["dep:fixed"]
125+
cordic = []
126+
fmac = []
112127
adc3 = []
113128
adc4 = []
114129
adc5 = []
@@ -174,6 +189,10 @@ required-features = ["usb"]
174189
name = "cordic"
175190
required-features = ["cordic"]
176191

192+
[[example]]
193+
name = "fmac"
194+
required-features = ["fmac"]
195+
177196
[[example]]
178197
name = "hrtim-adc-trigger"
179198
required-features = ["hrtim"]

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)