diff --git a/Cargo.toml b/Cargo.toml index cfe5af67..b3e2e217 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" } embedded-io = "0.6" stm32-hrtim = { version = "0.1.0", optional = true } @@ -82,9 +82,23 @@ usb = ["dep:stm32-usbd"] stm32g431 = ["stm32g4/stm32g431", "cat2"] stm32g441 = ["stm32g4/stm32g441", "cat2"] stm32g473 = ["stm32g4/stm32g473", "cat3", "adc3", "adc4", "adc5"] -stm32g474 = ["stm32g4/stm32g474", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g474"] +stm32g474 = [ + "stm32g4/stm32g474", + "cat3", + "adc3", + "adc4", + "adc5", + "stm32-hrtim/stm32g474", +] stm32g483 = ["stm32g4/stm32g483", "cat3", "adc3", "adc4", "adc5"] -stm32g484 = ["stm32g4/stm32g484", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g484"] +stm32g484 = [ + "stm32g4/stm32g484", + "cat3", + "adc3", + "adc4", + "adc5", + "stm32-hrtim/stm32g484", +] stm32g491 = ["stm32g4/stm32g491", "cat4", "adc3"] stm32g4a1 = ["stm32g4/stm32g4a1", "cat4", "adc3"] @@ -106,9 +120,10 @@ defmt = [ "embedded-hal/defmt-03", "embedded-io/defmt-03", "embedded-test/defmt", - "stm32-hrtim?/defmt" + "stm32-hrtim?/defmt", ] -cordic = ["dep:fixed"] +cordic = [] +fmac = [] adc3 = [] adc4 = [] adc5 = [] @@ -174,6 +189,10 @@ required-features = ["usb"] name = "cordic" required-features = ["cordic"] +[[example]] +name = "fmac" +required-features = ["fmac"] + [[example]] name = "hrtim-adc-trigger" required-features = ["hrtim"] diff --git a/Embed.toml b/Embed.toml new file mode 100644 index 00000000..74bc2415 --- /dev/null +++ b/Embed.toml @@ -0,0 +1,2 @@ +[default.rtt] +enabled = true diff --git a/examples/button.rs b/examples/button.rs index c3530b90..a1c12a9d 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -5,14 +5,13 @@ use stm32g4xx_hal::{ //delay::{DelayExt, SYSTDelayExt}, gpio::{self, ExtiPin, GpioExt, Input, SignalEdge}, rcc::RccExt, - stm32, - stm32::{interrupt, Interrupt}, + stm32::{self, interrupt, Interrupt}, syscfg::SysCfgExt, }; use core::cell::RefCell; use core::sync::atomic::{AtomicBool, Ordering}; -use cortex_m::{asm::wfi, interrupt::Mutex}; +use cortex_m::interrupt::Mutex; use cortex_m_rt::entry; type ButtonPin = gpio::PC13; @@ -52,8 +51,14 @@ fn main() -> ! { utils::logger::init(); let mut dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let mut rcc = dp.RCC.constrain(); - let mut syscfg = dp.SYSCFG.constrain(); + + // Workaround for RTT when using wfi instruction + // Enable an AHB peripheral clock for debug probe with wfi + rcc.ahb1enr().modify(|_, w| w.dma1en().set_bit()); + + let mut syscfg = dp.SYSCFG.constrain(&mut rcc); println!("Led Init"); // Configure PA5 pin to blink LED @@ -80,7 +85,7 @@ fn main() -> ! { println!("Start Loop"); loop { - wfi(); + cortex_m::asm::wfi(); println!("Check"); if G_LED_ON.load(Ordering::Relaxed) { diff --git a/examples/fmac.rs b/examples/fmac.rs new file mode 100644 index 00000000..dd273ac8 --- /dev/null +++ b/examples/fmac.rs @@ -0,0 +1,97 @@ +#![deny(warnings)] +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +use hal::prelude::*; +use hal::stm32; +use stm32g4xx_hal as hal; + +use cortex_m_rt::entry; + +#[macro_use] +mod utils; + +use utils::logger::info; + +use stm32g4xx_hal::fmac::{ + buffer::{BufferLayout, Watermark}, + function::{Gain, IIR}, + Buffer, FmacExt, +}; + +use fixed::types::I1F15; + +#[entry] +fn main() -> ! { + utils::logger::init(); + + info!("start"); + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let mut rcc = dp.RCC.constrain(); + + // The FMAC has an internal memory area of 256x16bit words that must be allocated to the + // three different buffers named X1 (input buffer), X2 (coefficients), and Y (output buffer). + // + // The BufferLayout struct takes three generic consts and will calculate the base offsets at compile time, + // which must be passed as a generic parameter to the constrain method of the FMAC instance. + // + // Create an FMAC instance with a memory layout of 32 inputs, 2 coefficients, and 1 output buffer words + // + // For IIR filters, RM0440 indicates X2 coeffecient buffer should be 2N + 2M where + // N is the number of feedforward coefficients and M is the number of feedback coefficients. + // + // Following constrain(), the buffer layout has been applied to the peripheral and cannot be changed. + let mut fmac = dp + .FMAC + .constrain::>(&mut rcc) + .reset(); + + // Configure an IIR filter with 1 feedforward, and 1 feedback coefficient, + // with both coefficients set to 1.0. + // + // This is equivalent to a multiply accumulate operation + // 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 + + // Fill the input buffer with 0.01 + fmac.preload_buffer(Buffer::X1, |_index| I1F15::from_num(0.01)); + + // Fill the coefficients with I1F15::MAX (max value representable by I1F15, ~1.0) + fmac.preload_buffer(Buffer::X2, |_index| I1F15::MAX); + fmac.preload_buffer(Buffer::Y, |_index| I1F15::ZERO); + + // Watermarks can be set on the X1 and Y buffers + // to control when the input empty and output full + // flags are asserted to cause early interrupts or DMA + // to avoid underflow or overflow conditions. + fmac.set_watermark(Buffer::X1, Watermark::Threshold1); + fmac.set_watermark(Buffer::Y, Watermark::Threshold1); + + // Select the IIR function, specifying the number of + // feedforward and feedback coefficients, and the gain + fmac.select_function(IIR { + feedforward_coeffs: 1, + feedback_coeffs: 1, + gain: Gain::ZeroDB, + }); + + let fmac = fmac.start(); + + info!("Input buffer full: {}", fmac.is_input_full()); + + info!("Waiting for result"); + while !fmac.is_result_available() {} + + info!("Reading results"); + + let mut count = 0; + loop { + while let Some(output) = fmac.read() { + count += 1; + info!("Output {}: {}", count, output.to_num::()); + } + } +} diff --git a/src/fmac.rs b/src/fmac.rs new file mode 100644 index 00000000..dde2a605 --- /dev/null +++ b/src/fmac.rs @@ -0,0 +1,380 @@ +//! Filter math accelerator (FMAC) peripheral +//! +//! The FMAC can perform FIR, IIR (direct form 1), and vector functions such as dot product + +use fixed::types::I1F15; + +use crate::{ + fmac::{ + buffer::{BufferLayoutConfig, Watermark}, + dma::{FmacDmaReader, FmacDmaWriter}, + function::{Function, LoadX1, LoadX2, LoadY}, + }, + rcc::Rcc, + stm32::FMAC, +}; +use core::marker::PhantomData; + +pub mod buffer; +pub mod dma; +pub mod function; +pub mod interrupt; + +pub trait State {} + +pub struct Stopped; +impl State for Stopped {} + +pub struct Running; +impl State for Running {} + +pub struct StoppedDma; +impl State for StoppedDma {} + +pub struct RunningDma; +impl State for RunningDma {} + +pub struct Fmac { + _phantom: PhantomData<(Layout, S)>, +} + +impl Fmac {} + +/// Buffer selection +pub enum Buffer { + X1, + X2, + Y, +} + +impl Fmac { + /// Start the FMAC in DMA mode, consuming self and return Fmac in the RunningDma state + #[inline(always)] + pub fn start_dma(self) -> Fmac { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.param().modify(|_, w| w.start().set_bit()); + Fmac::next_state() + } +} + +impl Fmac { + /// Configure the FMAC buffers using the layout provided to the constrain method + fn configure_buffers(&mut self) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.x1bufcfg().modify(|_, w| unsafe { + w.x1_base() + .bits(Layout::X1_BASE) + .x1_buf_size() + .bits(Layout::X1_SIZE) + }); + + fmac.x2bufcfg().modify(|_, w| unsafe { + w.x2_base() + .bits(Layout::X2_BASE) + .x2_buf_size() + .bits(Layout::X2_SIZE) + }); + + fmac.ybufcfg().modify(|_, w| unsafe { + w.y_base() + .bits(Layout::Y_BASE) + .y_buf_size() + .bits(Layout::Y_SIZE) + }); + } + + /// Preloads a [`Buffer`] with data. The closure is called to get the next element to write, + /// and will be called Layout::_SIZE times, expecting an I1F15 fixed point in return. + /// + /// This is only for initialization and should not be used during normal operation. + #[inline(always)] + pub fn preload_buffer(&mut self, buffer: Buffer, mut f: impl FnMut(u8) -> I1F15) { + let fmac = unsafe { &*FMAC::ptr() }; + + let word_count = match buffer { + Buffer::X1 => Layout::X1_SIZE, + Buffer::X2 => Layout::X2_SIZE, + Buffer::Y => Layout::Y_SIZE, + }; + + match buffer { + Buffer::X1 => self.select_function(LoadX1(word_count)), + Buffer::X2 => self.select_function(LoadX2(word_count, 0)), + Buffer::Y => self.select_function(LoadY(word_count)), + } + + // Set the start bit before writing data. This will be reset by hardware when the load operation is complete + fmac.param().modify(|_, w| w.start().set_bit()); + + for index in 0..word_count { + // Get a word to write from the closure + let word = f(index); + fmac.wdata() + .write(|w| unsafe { w.bits(word.to_bits() as u32) }); + } + + // Wait for the start bit to be cleared by hardware + while fmac.param().read().start().bit_is_set() {} + } + + /// Select the FMAC function, and set the input parameters + #[inline(always)] + pub fn select_function(&mut self, function: impl Function) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.param() + .modify(|_, w| unsafe { w.func().bits(function.id()) }); + + self.set_parameters( + function.p(), + function.q().unwrap_or(0), + function.r().unwrap_or(0), + ); + } + + /// Set FMAC input parameters. These are function dependent + #[inline(always)] + fn set_parameters(&mut self, p: u8, q: u8, r: u8) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.param() + .modify(|_, w| unsafe { w.p().bits(p).q().bits(q).r().bits(r) }); + } + + /// Set FMAC clipping mode + /// + /// true: Values from accumulator are saturated to the Q1.15 range [-1,1] + /// false: Values from accumulator will wrap + #[inline(always)] + pub fn set_clipping(&mut self, clipping: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.clipen().bit(clipping)); + } + + /// Set the [`Watermark`] level of the specified [`Buffer`] + /// + /// The X2 buffer does not support watermarks, as it is typically for + /// constant coefficients. It will silently ignore the watermark setting. + /// + /// For the X1 buffer, the watermark level defines the threshold for setting the Y1FULL flag. + /// + /// For the Y buffer, the watermark level defines the threshold for setting the YEMPTY flag. + /// It must be set to Threshold1 when using DMA mode. + #[inline(always)] + pub fn set_watermark(&mut self, buffer: Buffer, watermark: Watermark) { + let fmac = unsafe { &*FMAC::ptr() }; + match buffer { + Buffer::X1 => { + fmac.x1bufcfg() + .modify(|_, w| unsafe { w.full_wm().bits(watermark as u8) }); + } + Buffer::Y => { + fmac.ybufcfg() + .modify(|_, w| unsafe { w.empty_wm().bits(watermark as u8) }); + } + _ => {} + } + } + + /// Start the FMAC, consuming self and return Fmac in the Running state + #[inline(always)] + pub fn start(self) -> Fmac { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.param().modify(|_, w| w.start().set_bit()); + Fmac::next_state() + } + + /// Acquire DMA reader and writer handles for the FMAC. + /// This returns a tuple of Fmac in the StoppedDma state, FmacDmaReader, and FmacDmaWriter. + /// The start_dma() method must be called before the FMAC starts after the reader/writer have been assigned + /// to Transfers. + #[inline(always)] + pub fn acquire_dma(self) -> (Fmac, FmacDmaReader, FmacDmaWriter) { + let fmac = unsafe { &*FMAC::ptr() }; + + ( + Fmac::next_state(), + FmacDmaReader::new(fmac.rdata() as *const _ as *mut u32), + FmacDmaWriter::new(fmac.wdata() as *const _ as *mut u32), + ) + } +} + +impl Fmac { + /// Transition to the next state + fn next_state() -> Self { + Fmac { + _phantom: PhantomData, + } + } + + /// Reset the FMAC. Resets read pointers, internal logic, + /// status register, and clears parameters. + /// + /// Following a reset, the FMAC is in the stopped state, + /// and can be restarted again by selecting a new Function + /// and issuing a start. + #[inline(always)] + pub fn reset(self) -> Fmac { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.reset().set_bit()); + // Wait for reset to be cleared by hardware + while fmac.cr().read().reset().bit_is_set() {} + Fmac::next_state() + } + + /// Get the saturation error status. + /// If this bit is set, it can only be cleared by resetting the FMAC. + #[inline(always)] + pub fn saturation_error_status(&self) -> bool { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.sr().read().sat().bit_is_set() + } + + /// Get the underflow error status. + /// If this bit is set, it can only be cleared by resetting the FMAC. + #[inline(always)] + pub fn underflow_error_status(&self) -> bool { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.sr().read().unfl().bit_is_set() + } + + /// Get the ovrerflow error status. + /// If this bit is set, it can only be cleared by resetting the FMAC. + #[inline(always)] + pub fn overflow_error_status(&self) -> bool { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.sr().read().ovfl().bit_is_set() + } + + /// Get the X1 Buffer full status. + /// + /// If the flag is set, the X1 buffer is full and no more data can be written to it. + /// The flag is set and cleared by hardware, or a reset. + #[inline(always)] + pub fn x1_full_status(&self) -> bool { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.sr().read().x1full().bit_is_set() + } + + /// Get the Y Buffer empty status + /// + /// If the flag is set, the Y buffer is empty and no more data can be read from it. + /// The flag is set and cleared by hardware, or a reset. + #[inline(always)] + pub fn y_empty_status(&self) -> bool { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.sr().read().yempty().bit_is_set() + } + + /// Enable read DMA from the RDATA register + #[inline(always)] + pub fn enable_read_dma(&mut self, enable: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.dmaren().bit(enable)); + } + + /// Enable write DMA to the WDATA register + #[inline(always)] + pub fn enable_write_dma(&mut self, enable: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.dmawen().bit(enable)); + } +} + +impl Fmac { + #[inline(always)] + pub fn write(&mut self, data: I1F15) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.wdata() + .write(|w| unsafe { w.bits(data.to_bits() as u32) }); + } +} + +impl Fmac { + /// Stop the FMAC operation, clearing the START bit in the PARAM register. + /// Consumes self and returns Fmac in the Stopped state, where it can be reconfigured or started again. + /// + /// # Returns + /// + /// * Fmac in the Stopped state + pub fn stop(self) -> Fmac { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.param().modify(|_, w| w.start().clear_bit()); + Fmac::next_state() + } + + /// Check if there is a result available in the output buffer. + /// + /// # Returns + /// + /// * true if there are pending results which can be read + /// * false if the Y buffer is empty + pub fn is_result_available(&self) -> bool { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.sr().read().yempty().bit_is_clear() + } + + /// Check if the input buffer (X1) is full + /// + /// # Returns + /// + /// * true if the input buffer is full + /// * false if the input buffer is not full + pub fn is_input_full(&self) -> bool { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.sr().read().x1full().bit_is_set() + } + + /// Read the result from the RDATA register. + /// The Y buffer read pointer is automatically incremented on each read. + /// + /// # Returns + /// + /// * None if the Y buffer is empty + /// * Some Fixed point Q1.15 value result as [`fixed::I1F15`] type + #[inline(always)] + pub fn read(&self) -> Option { + if self.is_result_available() { + let fmac = unsafe { &*FMAC::ptr() }; + let result = fmac.rdata().read().bits() as i16; + Some(I1F15::from_bits(result)) + } else { + None + } + } + + /// Write input data to the WDATA register. + /// The X1 buffer write pointer is automatically incremented on each write. + /// + /// # Arguments + /// + /// * `data` - The input data to write to the FMAC. + #[inline(always)] + pub fn write(&mut self, data: I1F15) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.wdata() + .write(|w| unsafe { w.bits(data.to_bits() as u32) }); + } +} + +/// Extension trait for constraining the FMAC peripheral. +pub trait FmacExt { + /// Constrain the FMAC peripheral. + /// + /// # Arguments + /// + /// * `rcc` - The RCC peripheral for enabling the FMAC clock. + fn constrain(self, rcc: &mut Rcc) -> Fmac; +} + +impl FmacExt for FMAC { + fn constrain(self, rcc: &mut Rcc) -> Fmac { + rcc.rb.ahb1rstr().modify(|_, w| w.fmacrst().set_bit()); + rcc.rb.ahb1rstr().modify(|_, w| w.fmacrst().clear_bit()); + + rcc.rb.ahb1enr().modify(|_, w| w.fmacen().set_bit()); + + let mut fmac = Fmac::::next_state().reset(); + fmac.configure_buffers(); + fmac + } +} diff --git a/src/fmac/buffer.rs b/src/fmac/buffer.rs new file mode 100644 index 00000000..28937a19 --- /dev/null +++ b/src/fmac/buffer.rs @@ -0,0 +1,54 @@ +//! FMAC Buffer configuration + +/// Buffer watermark thresholds +#[repr(u8)] +pub enum Watermark { + Threshold1 = 0, + Threshold2 = 1, + Threshold4 = 2, + Threshold8 = 3, +} + +/// FMAC Buffer layout configuration trait +pub trait BufferLayoutConfig { + const X1_BASE: u8; + const X2_BASE: u8; + const Y_BASE: u8; + + const X1_SIZE: u8; + const X2_SIZE: u8; + const Y_SIZE: u8; + + // Verify that the total size doesn't exceed FMAC memory + const _ASSERT_SIZE: () = assert!( + (Self::Y_BASE + Self::Y_SIZE) as u16 <= 256, + "Total buffer size exceeds FMAC memory capacity" + ); +} + +/// FMAC buffer layout +/// +/// Note: For IIR filters, minimum coefficient size is 2N + 2M +/// where N is the number of feedforward coefficients and M is the number of feedback coefficients. +/// +/// The base address of the layout can be specified, and defaults to 0. +/// This allows for more flexibility in memory allocation for configuring +/// multiple layouts within the FMAC memory. +pub struct BufferLayout< + const INPUT_SIZE: u8, + const COEFFICIENT_SIZE: u8, + const OUTPUT_SIZE: u8, + const BASE: u8 = 0, +>; + +impl + BufferLayoutConfig for BufferLayout +{ + const X1_BASE: u8 = BASE; + const X2_BASE: u8 = Self::X1_BASE + INPUT_SIZE; + const Y_BASE: u8 = Self::X2_BASE + COEFFICIENT_SIZE; + + const X1_SIZE: u8 = INPUT_SIZE; + const X2_SIZE: u8 = COEFFICIENT_SIZE; + const Y_SIZE: u8 = OUTPUT_SIZE; +} diff --git a/src/fmac/dma.rs b/src/fmac/dma.rs new file mode 100644 index 00000000..52a12bfe --- /dev/null +++ b/src/fmac/dma.rs @@ -0,0 +1,55 @@ +//! DMA support for the FMAC peripheral. + +use embedded_dma::ReadBuffer; + +use crate::dma::{ + mux::DmaMuxResources, traits::TargetAddress, MemoryToPeripheral, PeripheralToMemory, +}; + +/// Write handle for DMA transfers to the FMAC peripheral. +pub struct FmacDmaWriter { + wdata: *mut u32, +} + +impl FmacDmaWriter { + pub(crate) fn new(wdata: *mut u32) -> Self { + Self { wdata } + } +} + +/// Read handle for DMA transfers from the FMAC peripheral. +pub struct FmacDmaReader { + rdata: *mut u32, +} + +impl FmacDmaReader { + pub(crate) fn new(rdata: *mut u32) -> Self { + Self { rdata } + } +} + +unsafe impl TargetAddress for FmacDmaReader { + type MemSize = u32; + const REQUEST_LINE: Option = Some(DmaMuxResources::FMAC_Read as u8); + + fn address(&self) -> u32 { + self.rdata as u32 + } +} + +unsafe impl ReadBuffer for FmacDmaReader { + type Word = u32; + + unsafe fn read_buffer(&self) -> (*const Self::Word, usize) { + (self.rdata as *const u32, 1) + } +} + +unsafe impl TargetAddress for FmacDmaWriter { + type MemSize = u32; + const REQUEST_LINE: Option = Some(DmaMuxResources::FMAC_Write as u8); + + fn address(&self) -> u32 { + self.wdata as u32 + } +} diff --git a/src/fmac/function.rs b/src/fmac/function.rs new file mode 100644 index 00000000..d136490a --- /dev/null +++ b/src/fmac/function.rs @@ -0,0 +1,127 @@ +//! FMAC Functions + +/// Programmable gain parameter in the range 0dB to 42dB in 6dB increments. +#[repr(u8)] +#[derive(Copy, Clone)] +pub enum Gain { + ZeroDB = 0, + SixDB = 1, + TwelveDB = 2, + EighteenDB = 3, + TwentyFourDB = 4, + ThirtyDB = 5, + ThirtySixDB = 6, + FortyTwoDB = 7, +} + +/// FMAC Function trait. This defines the function specific data that is loaded into the PARAM register +pub trait Function { + /// The function ID loaded into the FUNC field + const ID: u8; + + #[inline(always)] + fn id(&self) -> u8 { + Self::ID + } + + /// Returns the value of the P parameter + fn p(&self) -> u8; + + /// Returns the value of the Q parameter, or None if not used + fn q(&self) -> Option { + None + } + + /// Returns the value of the R parameter, or None if not used + fn r(&self) -> Option { + None + } +} + +/// Load X1 buffer with specified length +pub struct LoadX1(pub u8); +/// Load X2 buffer with length of first set, and second set of coefficients +/// The second length may be zero if the second set of coefficients is not used. +pub struct LoadX2(pub u8, pub u8); +/// Load Y buffer with specified length +pub struct LoadY(pub u8); + +impl Function for LoadX1 { + const ID: u8 = 1; + + fn p(&self) -> u8 { + self.0 + } +} + +impl Function for LoadX2 { + const ID: u8 = 2; + + fn p(&self) -> u8 { + self.0 + } + + fn q(&self) -> Option { + Some(self.1) + } +} + +impl Function for LoadY { + const ID: u8 = 3; + + fn p(&self) -> u8 { + self.0 + } +} + +/// FIR / Convolution / Dot Product function +pub struct Convolution { + /// Number of coefficients in the X2 buffer + pub length: u8, + /// Gain parameter in the range 0dB to 42dB in 6dB increments + pub gain: Gain, +} + +/// Finite Impulse Response (FIR) / Convolution / Dot Product function +impl Function for Convolution { + const ID: u8 = 8; + + #[inline(always)] + fn p(&self) -> u8 { + self.length + } + + #[inline(always)] + fn r(&self) -> Option { + Some(self.gain as u8) + } +} + +/// Infinite Impulse Response (IIR) filter / Multiply Accumulate +pub struct IIR { + /// The number of feedforward coefficients in the X2 buffer + /// X2 buffer size should be at least 2*feedforward_coeffs + 2*feedback_coeffs + pub feedforward_coeffs: u8, + /// The number of feedback coefficients in the X2 buffer + /// X2 buffer size should be at least 2*feedforward_coeffs + 2*feedback_coeffs + pub feedback_coeffs: u8, + /// Gain parameter in the range 0dB to 42dB in 6dB increments + pub gain: Gain, +} + +impl Function for IIR { + const ID: u8 = 9; + + #[inline(always)] + fn p(&self) -> u8 { + self.feedforward_coeffs + } + + fn q(&self) -> Option { + Some(self.feedback_coeffs) + } + + fn r(&self) -> Option { + Some(self.gain as u8) + } +} diff --git a/src/fmac/interrupt.rs b/src/fmac/interrupt.rs new file mode 100644 index 00000000..b0da93b7 --- /dev/null +++ b/src/fmac/interrupt.rs @@ -0,0 +1,43 @@ +use super::buffer::BufferLayoutConfig; +use super::Fmac; +use super::State; +use crate::stm32::FMAC; + +impl Fmac { + /// Enable saturation error interrupt + #[inline(always)] + pub fn enable_saturation_error_interrupt(&mut self, enable: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.satien().bit(enable)); + } + + /// Enable underflow error interrupt + #[inline(always)] + pub fn enable_underflow_error_interrupt(&mut self, enable: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.unflien().bit(enable)); + } + + /// Enable overflow error interrupt + #[inline(always)] + pub fn enable_overflow_error_interrupt(&mut self, enable: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.ovflien().bit(enable)); + } + + /// Enable write interrupts. The write interrupt is raised when the X1 (operand) + /// buffer is not full, and there is space available to write to the FMAC. + #[inline(always)] + pub fn enable_write_interrupt(&mut self, enable: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.wien().bit(enable)); + } + + /// Enable read interrupts. The read interrupt is raised when the Y (result) + /// buffer is not empty, and there is data available to read from the FMAC. + #[inline(always)] + pub fn enable_read_interrupt(&mut self, enable: bool) { + let fmac = unsafe { &*FMAC::ptr() }; + fmac.cr().modify(|_, w| w.rien().bit(enable)); + } +} diff --git a/src/lib.rs b/src/lib.rs index c8b663b4..95c0610a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,6 +69,8 @@ pub mod can; pub mod comparator; #[cfg(feature = "cordic")] pub mod cordic; +#[cfg(feature = "fmac")] +pub mod fmac; // pub mod crc; pub mod dac; pub mod delay; diff --git a/src/syscfg.rs b/src/syscfg.rs index b13833bb..097f6850 100644 --- a/src/syscfg.rs +++ b/src/syscfg.rs @@ -1,25 +1,17 @@ -use crate::bb; -use crate::stm32::{RCC, SYSCFG}; +use crate::rcc::Rcc; +use crate::stm32::SYSCFG; use core::ops::Deref; /// Extension trait that constrains the `SYSCFG` peripheral pub trait SysCfgExt { /// Constrains the `SYSCFG` peripheral so it plays nicely with the other abstractions - fn constrain(self) -> SysCfg; + fn constrain(self, rcc: &mut Rcc) -> SysCfg; } impl SysCfgExt for SYSCFG { - fn constrain(self) -> SysCfg { - unsafe { - // NOTE(unsafe) this reference will only be used for atomic writes with no side effects. - let rcc = &(*RCC::ptr()); - - // Enable clock. - bb::set(&rcc.apb2enr(), 0); - - // Stall the pipeline to work around erratum 2.1.13 (DM00037591) - cortex_m::asm::dsb(); - } + fn constrain(self, rcc: &mut Rcc) -> SysCfg { + // Enable SYSCFG peripheral clock in APB2ENR register + rcc.apb2enr().modify(|_, w| w.syscfgen().set_bit()); SysCfg(self) }