Skip to content

Add FMAC implementation #214

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open

Conversation

boondocklabs
Copy link
Contributor

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


/// Enable write DMA to the WDATA register
#[inline(always)]
pub fn enable_write_dma(&mut self, enable: bool) {
Copy link
Member

@usbalbin usbalbin Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this relate to acquire_dma and start_dma? Why is it available in non-dma modes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The enable_x_dma() are to set the enable DMA bits in the peripheral.

acquire_dma() returns a tuple of:

  • Fmac in StoppedDma state which exposes start_dma() to start the FMAC
  • an FmacDmaReader which can be consumed by a transfer, and
  • an FmacDmaWriter which can be consumed by another transfer.

The Fmac handle in StoppedDma can then start the FMAC after DMA has been configured by calling start_dma() which is only exposed in StoppedDma state.

So if you just want say write DMA, it would be something like

let fmac = Fmac::constrain<BufferLayout<8,8,8>>(&mut rcc);

// Acquire split DMA read/write handles - discarding the read handle.
let (fmac, _, write_dma) = fmac.acquire_dma();

// Tell FMAC to generate write DMA requests
fmac.enable_write_dma();

// Give the DMA write handle to a transfer 
let transfer = dma_ch.into_memory_to_peripheral_transfer(write_dma, ...);

// Now start the FMAC
fmac.start_dma();

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks, that makes sense. So the peripheral is still useful for non-dma use if the user so chooses? As in this case the user can still read from the fmac and only use the dma for writing?

I assume there is nothing preventing the user from writing the fmac at the same time as the dma is enabled. Are there any risks with that?

@usbalbin
Copy link
Member

Very cool! :)

Copy link
Contributor

@AdinAck AdinAck left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool! Just one comment from me.

I have never used the FMAC so I can't verify that the hardware invariances are exhaustively upheld by this implementation.

@@ -17,7 +17,7 @@ stm32g4 = { version = "0.16.0", features = ["atomics"] }
paste = "1.0"
fugit = "0.3.7"
stm32-usbd = { version = "0.7.0", optional = true }
fixed = { version = "1.28.0", optional = true }
fixed = { version = "1.28.0" }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why make this no longer optional?

Copy link
Contributor Author

@boondocklabs boondocklabs Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually originated from DualDac PR, which wasn't feature gated and depends on fixed::types - not strictly necessary for this PR, but if DualDac is merged as-is, it wouldn't be optional. Could either feature gate dualdac or just leave this as is

Comment on lines +109 to +126
cordic = []
fmac = []
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
cordic = []
fmac = []
cordic = ["dep:fixed"]
fmac = ["dep:fixed"]

@boondocklabs
Copy link
Contributor Author

I have never used the FMAC so I can't verify that the hardware invariances are exhaustively upheld by this implementation.

Yes, only tested so far on G474VET6. I have some 474CET and RET, and G431s I can test on when I find some cycles.

// Y[n] = ∑ X1[n] * X2[0] + Y[n-1] * X2[1]
//
// The inputs will be set to ~0.01 (to nearest fixed point representation), and the output will increment
// by 0.01 into Y for each input sample, and the final read of Y should be about 0.319
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it would make sense to add something like this as a test in here or here?

boondocklabs and others added 9 commits July 21, 2025 18:00
The `SysCfg` peripheral used bit banding to set the APB2 peripheral clock enable bit which fails on an assertion on a G431KBT (have not tested on other chips, but I suspect they would do the same). This takes a mutable reference to `Rcc` in `SysCfg::constrain` and uses safe accessors to enable the clock.

Updated and tested the button example which is all that uses SysCfg.

```[INFO ] Configuring PLL (stm32_foc stm32-foc/src/main.rs:132)
[INFO ] System clock frequency: 168000000 (stm32_foc stm32-foc/src/main.rs:138)
[DEBUG] Write 20007FB0 (stm32g4xx_hal stm32g4xx-hal/src/bb.rs:42)
[ERROR] panicked at /Users/fuzz/wave/stm32g4xx-hal/src/bb.rs:44:5:
assertion failed: (PERI_ADDRESS_START..=PERI_ADDRESS_END).contains(&addr) (panic_probe panic-probe-1.0.0/src/lib.rs:104)
Firmware exited unexpectedly: Multiple
Core 0
    Frame 0: HardFault_ @ 0x08006394
       /Users/fuzz/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/cortex-m-rt-0.7.5/src/lib.rs:1103:1
    Frame 1: HardFault <Cause: Escalated UsageFault <Cause: Undefined instruction>> @ 0x08005ce2```
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
Co-authored-by: Albin Hedman <[email protected]>
* Make next_state() private
* Added Gain enum and doc comments
* Changed Function args to use Gain enum rather than u8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants